diff --git a/_posts/2013-11-20-a-blog-thats-not-a-blog.md b/_posts/2013-11-20-a-blog-thats-not-a-blog.md
new file mode 100644
index 000000000..ac6a73677
--- /dev/null
+++ b/_posts/2013-11-20-a-blog-thats-not-a-blog.md
@@ -0,0 +1,56 @@
+---
+layout: post
+title: "A Blog That's Not A Blog"
+date: 2013-11-20 11:00:00
+category: News
+permalink: /blog/2013/11/20/
+---
+
+As you may have noticed (or not), the [JSMachines](http://jsmachines.net/) website had a very modest makeover
+recently.
+
+Originally, the site was a smattering of HTML files, along with some XML files that I was rendering as HTML
+using some simple XSL stylesheets. However, I was tired of having one set of files for the website to explain
+things and a different set of files on [GitHub](http://github.com) that explained other things -- many
+of those things being the SAME things.
+
+So this month, I decided to eliminate all the HTML files. As you browse the site, you're simply navigating
+folders from the **GitHub** project and reading the project's **README.md** files.
+
+There's a single PHP script responsible for transforming a folder's default document (either **README.md**
+or **machine.xml**) to HTML, as well as displaying the current directory across the top and a directory listing
+down the left-hand side.
+
+The same script provides support for a subset of the [Markdown](http://daringfireball.net/projects/markdown/)
+syntax, which is more than sufficient to handle all the site's **README.md** files. I probably should
+have used a third-party Markdown library, but this was more educational, and it was easy to add extra features,
+like the ability to embed JavaScript machines with a single Markdown-style link; eg:
+
+ [IBM PC](/devices/pcx86/machine/5150/mda/64kb/ "PCjs:ibm5150")
+
+The script takes care of the rest, adding the appropriate stylesheets and PCjs scripts automatically.
+
+I had more grandiose plans, including a command-line prompt written in JavaScript that would allow you to
+navigate the site exactly as you would an IBM PC hard drive from a "DOS prompt", and I may try something
+like that later, but don't hold your breath.
+
+I've tried to improve the organization of all the [Machine Configuration Files](/devices/pcx86/machine/) as well.
+The variety of configurations was getting out of hand. It's a bit tidier now, but there's still room for
+improvement.
+
+My workflow is improving, too. I'm more comfortable with [GitHub](http://github.com) now,
+and I recently switched from Eclipse to JetBrains' [WebStorm](http://www.jetbrains.com/webstorm) (well,
+actually [PhpStorm](http://www.jetbrains.com/phpstorm), since it's a superset of WebStorm, although I did start
+with WebStorm), and the new development environment is feeling pretty good now. I've had zero problems with
+[JetBrains](http://www.jetbrains.com) products and I'm seriously impressed with their quality and completeness,
+so I have no qualms about moving from the "free" Eclipse platform to the $99 PhpStorm IDE.
+
+The nice thing about the new GitHub-centric approach is that it's easy to "push" changes to both the repository
+and the website. I update one or more **README.md** files, "Commit and Push" from the IDE, then "pull" from GitHub
+on the web server.
+
+This so-called blog is more of the same: **README.md** files in a series of folders. The only question now:
+will this actually evolve into a series...?
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*November 20, 2013*
diff --git a/_posts/2014-01-01-new-year-new-directions.md b/_posts/2014-01-01-new-year-new-directions.md
new file mode 100644
index 000000000..0758c2455
--- /dev/null
+++ b/_posts/2014-01-01-new-year-new-directions.md
@@ -0,0 +1,19 @@
+---
+layout: post
+title: New Year, New Directions
+date: 2014-01-01 11:00:00
+category: Goals
+permalink: /blog/2014/01/01/
+---
+
+Initial goals for 2014 include
+
+- Setting up a new web server running node.js, using either AWS, Google Compute Engine or Windows Azure;
+- Porting this project to work with node.js, which includes rewriting all the PHP code as server-side JavaScript;
+- Deciding whether to separate PCjs from C1Pjs for the new web site, or simply make PCjs.org a mirror of jsmachines.net;
+- Deciding how best (or even whether) to accomodate PCjs running from both **node** and **non-node** web servers.
+
+I guess we'll learn more as the year progresses.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*January 20, 2014*
diff --git a/_posts/2014-03-30-running-on-azure.md b/_posts/2014-03-30-running-on-azure.md
new file mode 100644
index 000000000..02521f5ec
--- /dev/null
+++ b/_posts/2014-03-30-running-on-azure.md
@@ -0,0 +1,56 @@
+---
+layout: post
+title: Running on Azure
+date: 2014-03-30 11:00:00
+category: Web Servers
+permalink: /blog/2014/03/30/
+---
+
+Publishing a Node-based site to [Azure](http://azure.com) was painless, thanks to their friendly web portal and
+GitHub integration. Getting a fully operational site, however, took a bit more time.
+
+Unfortunately, because Azure's underlying server technology is Windows-based (IIS), some of the same Windows/Unix
+portability problems that have plagued us for decades still plague us today: **carriage returns** and **backslashes**.
+
+Even though all PCjs text files in my project contain only linefeeds, IIS would serve them up with CR/LF
+instead. I first noticed this on the client side, when an XML file retrieved via *XMLHttpRequest()* came back
+full of CR/LFs, and later on the server side, when *fs.readFile()* returned a Markdown file filled with CR/LFs.
+
+I wondered if the CR/LF transformation had happened when Azure pulled all my files from GitHub, because
+while I can understand some whitespace inconsistencies across web servers, I would never expect file system calls
+on the server to modify file contents.
+
+I finally confirmed that the files were indeed modified on the server, by using Azure's FTP browser. For example,
+[us83-buttons-minimal.xml](/devices/pcx86/keyboard/us83-buttons-minimal.xml) is currently 622 bytes locally, but on the
+Azure server, the reported size is 632 bytes -- one extra CR for each of the file's 10 lines. After a little more
+digging, I [learned something new](http://git-scm.com/book/ch7-1.html#Formatting-and-Whitespace) about **Git**: it
+has a setting called `core.autocrlf` which, for me on OS X, defaults to `input` (meaning "convert CR/LF to LF on commit
+but do NOT convert LF back to CR/LF on check-out"). But Azure apparently sets this to `true`, causing all LFs to be
+converted to CR/LF.
+
+Regarding slashes, even when path components contained only slashes, *path.join()* would return paths with
+backslashes. And unfortunately, this behavior varies from Node module to module. For example, I use the NPM
+[glob](https://www.npmjs.org/package/glob) module, and even when the input path to *glob()* contains backslashes,
+its output paths do not.
+
+In the process of fixing those portability issues, I also had some trouble getting Azure logging to work as
+documented. Setting `loggingEnabled: true` in **/IISNode.yml** would generate logs in **/site/wwwroot/iisnode/**,
+but the logs were numerous and poorly organized.
+
+And the Azure command-line tool that was *supposed* to enable real-time log-streaming to the console:
+
+ azure site log tail pcjs
+
+would happily report:
+
+ Welcome, you are now connected to log-streaming service
+
+but it would NEVER display anything but deployment information. Instead, I had to browse the log files using Azure's
+"FTP DIAGNOSTIC LOGS" link on the web portal -- which presents the logs as one big, ugly, fragmented mess:
+
+
+
+Sigh. But at least the site is up and fully operational now.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*March 30, 2014*
diff --git a/_posts/2014-03-31-browser-compatibility-woes.md b/_posts/2014-03-31-browser-compatibility-woes.md
new file mode 100644
index 000000000..a58e37b90
--- /dev/null
+++ b/_posts/2014-03-31-browser-compatibility-woes.md
@@ -0,0 +1,47 @@
+---
+layout: post
+title: Browser Compatibility Woes
+date: 2014-03-31 11:00:00
+category: JavaScript
+permalink: /blog/2014/03/31/
+---
+
+While JavaScript has been doing a good job of delivering on the old "write once, run everywhere" promise that its
+[unrelated namesake](http://www.java.com) coined, the "hook once, deliver everywhere" promise seems less fulfilled.
+
+Not that anyone ever made such a promise.
+
+Specifically, I'm talking about DOM events. Take the HTML5 <canvas> element, for example. If I give
+it a `contenteditable="true"` attribute, it will play nicely with my JavaScript app in a Mobile Safari browser,
+by popping up the device's soft keyboard in response to the canvas element receiving focus (ie, when you tap on it).
+
+The Silk browser on a Kindle Fire, however, is another story -- it acts like it has no idea what `contenteditable`
+means. Even if I display an actual <input> text field alongside the <canvas>, and attach all the same
+input event handlers to the text field instead of the canvas, the Kindle Fire's soft keyboard will pop up, but my
+input event handlers still won't fire.
+
+I've done what testing I can with the Android SDK and the "Android Virtual Device Manager", and in general, support
+looks fine -- you click/tap on the PC's screen, the soft keyboard pops up, and typing works. So I guess Silk is just
+an outlier. I'm not sure what I'll do about it yet (or even what I can do).
+
+
+
+---
+
+Here's another issue I've yet to resolve.
+
+When Apple released iOS 7.0, PCjs went from being a rock-solid web application on
+2nd/3rd/4th-generation iPads to a very flaky web application. It's still rock-solid on 5th-generation iPads
+(the iPad Air and iPad Mini w/Retina Display), and so I suspect a bug in Apple's JavaScript engine that's specific
+to their older A5 processor.
+
+It may be possible to work around the bug, but I haven't yet isolated exactly what code
+sequence(s) are failing. For now, this is the most serious unresolved PCjs bug I'm aware of.
+
+---
+
+If you're having a problem (or trouble with a device) that I've not already mentioned, [let me know](mailto:Jeff@pcjs.org).
+Thanks.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*March 31, 2014*
diff --git a/_posts/2014-04-01-the-latest-in-emulator-technology.md b/_posts/2014-04-01-the-latest-in-emulator-technology.md
new file mode 100644
index 000000000..a33a6b948
--- /dev/null
+++ b/_posts/2014-04-01-the-latest-in-emulator-technology.md
@@ -0,0 +1,23 @@
+---
+layout: post
+title: The Latest in Emulator Technology
+date: 2014-04-01 11:00:00
+category: JavaScript
+permalink: /blog/2014/04/01/
+---
+
+Announcing **InternetJS: The Internet Emulator**, the world's smallest JavaScript application capable of emulating the entire Internet.
+And like all PCjs applications, there's nothing to install. It runs safely and securely from any web browser.
+
+Check out the ALPHA release demo below.
+
+
+
+Disclaimer: InternetJS may not be suitable for everyone. Ask your doctor if InternetJS is right for you. Side-effects may include:
+
+- Increased awareness
+- Loss of appetite after eating large meals
+- Inability to forget things you never wanted to remember
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*April 1, 2014*
diff --git a/_posts/2014-04-12-whats-new-in-1.13.0.md b/_posts/2014-04-12-whats-new-in-1.13.0.md
new file mode 100644
index 000000000..811ca4011
--- /dev/null
+++ b/_posts/2014-04-12-whats-new-in-1.13.0.md
@@ -0,0 +1,26 @@
+---
+layout: post
+title: "What's New in 1.13.0"
+date: 2014-04-12 11:00:00
+category: Releases
+permalink: /blog/2014/04/12/
+---
+
+The latest version adds support for "software manifests", which you can read more about [here](/apps/). Basically, manifests
+are simple XML files that describe a piece of software (an application, an operating system, whatever). They can also
+link to a PCjs machine configuration capable of running the software, along with a "ready-to-run" machine state file.
+Conversely, a PCjs machine XML file can refer back to the manifest, to obtain a list of disk images.
+
+Here are some [Demos](/apps/pc/) of "ready-to-run" apps on [PCjs](/docs/about/).
+
+There have been lots of server-side changes recently, including API improvements that make it easy (well, *easier*)
+to dynamically create diskette images from a list of files, or even an entire folder (including all subfolders),
+as long as the total size of all the files will fit on a PCjs-supported diskette image. Support for creating hard disk
+images is still on the "TODO" list (the original **convdisk** PHP script supported hard disk images, but that functionality
+hasn't been ported to the newer **diskdump** Node module yet).
+
+Almost nothing has changed in the PCjs client-side code (which is where the emulator runs), except for changes to use
+the new **diskdump** API.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*April 12, 2014*
diff --git a/_posts/2014-04-14-node-express-safari.md b/_posts/2014-04-14-node-express-safari.md
new file mode 100644
index 000000000..d9f395245
--- /dev/null
+++ b/_posts/2014-04-14-node-express-safari.md
@@ -0,0 +1,156 @@
+---
+layout: post
+title: "Node + Express != Safari"
+date: 2014-04-14 11:00:00
+category: Browsers
+permalink: /blog/2014/04/14/
+---
+
+There's something very odd going on with between Node+Express and Safari, resulting in blank web pages.
+Don't believe me? Just ask [Google](https://www.google.com/#q=node+express+safari+blank+page).
+
+[{{ site.pcjs.domain }}]({{ site.url }}/) contains a lot of XML files that are rendered as web pages using XML
+stylesheets. And occasionally Safari -- and ONLY Safari -- will render those XML files as blank pages.
+
+For example, here's the
+[machine.xml](/devices/pcx86/machine/5150/mda/64kb/machine.xml) file that's also embedded on the
+[{{ site.pcjs.domain }}]({{ site.url }}/) home page.
+
+When Safari fetched that XML file from an Apache web server (what I used before switching to Node),
+the request would look like:
+
+ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+ Cache-Control: max-age=0
+ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
+
+and the response would look like:
+
+ Date: Mon, 14 Apr 2014 22:11:20 GMT
+ Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
+ Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.24 mod_ssl/2.2.26 OpenSSL/0.9.8y
+ Etag: "37a10b3-492-4f6e2e96b2e80"
+ Content-Type: text/xml
+ Connection: Keep-Alive
+ Accept-Ranges: bytes
+ Keep-Alive: timeout=5, max=100
+ Content-Length: 1170
+
+with a status code of 200 ("OK"). And no matter how many times I hit Safari's Reload button, the response was the same.
+
+Now with Node+Express, the same exact request would look like:
+
+ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
+
+with a response of:
+
+ Date: Mon, 14 Apr 2014 22:16:42 GMT
+ Etag: "1170-1397354346000"
+ Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
+ X-Powered-By: Express
+ Content-Type: application/xml
+ Cache-Control: public, max-age=0
+ Connection: keep-alive
+ Accept-Ranges: bytes
+ Content-Length: 1170
+
+HOWEVER, as soon as I used Safari's Back button to return to the home page, and then pressed the Forward button to return to
+the XML file, the XML request changed to:
+
+ Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+ Cache-Control: max-age=0
+ If-None-Match: "1170-1397354346000"
+ If-Modified-Since: Sun, 13 Apr 2014 01:59:06 GMT
+ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
+
+with a response of 304 ("Not Modified") and the following response headers:
+
+ Date: Mon, 14 Apr 2014 22:18:26 GMT
+ Cache-Control: public, max-age=0
+ Etag: "1170-1397354346000"
+ Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
+ Connection: keep-alive
+ Accept-Ranges: bytes
+ X-Powered-By: Express
+
+And here's where the "blank page" problem occurs: pressing Safari's Reload button. Again, the request looks the same as before,
+and the response is still 304 ("Not Modified"), but the page is blank, and the response now looks like:
+
+ Date: Mon, 14 Apr 2014 22:21:15 GMT
+ Cache-Control: public, max-age=0
+ Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
+ Connection: keep-alive
+ Accept-Ranges: bytes
+ X-Powered-By: Express
+ Etag: "1170-1397354346000"
+
+and no matter how many times I press Reload, the response is the same (except for an updated *Date*), and the page is still blank.
+
+So, here's the kludge I've added to my Express server code, to prevent Safari from displaying blank pages for those XML files:
+
+{% highlight javascript linenos %}
+ /*
+ * The Safari "blank page" problem continues to plague us. Our first work-around was for directory
+ * "index.html" documents, which we resolved by always sending the document ourselves, along with an
+ * "ok" (200) response, instead of letting next() handle it, which would result in a "not modified"
+ * (304) response.
+ *
+ * However, the problem also extends to any XML files that we serve to an initial Safari request
+ * (eg, the machine.xml and manifest.xml files that we style as web pages). Safari includes
+ * "Cache-Control max-age=0" in the request, and if the response is "Cache-Control public, max-age=0"
+ * along with a 304 response code, Safari may once again display a blank page.
+ *
+ * This problem appears limited to the initial resource request for a particular URL. When these XML
+ * files are requested by Safari while loading another web page, Safari's caching logic is different
+ * (eg, it doesn't include the same "Cache-Control" setting).
+ */
+ if (sBaseName == "machine.xml" || sBaseName == "manifest.xml") {
+ var sAgent = req.headers['user-agent'];
+ if (sAgent && sAgent.indexOf("Safari/") >= 0 && sAgent.indexOf("Chrome/") < 0 && sAgent.indexOf("OPR/") < 0) {
+ var sCacheControl = req.headers['cache-control'];
+ if (sCacheControl && sCacheControl.indexOf("max-age=0") >= 0) {
+ fs.readFile(sPath, {encoding: "utf8"}, function doneReadFile(err, sData) {
+ if (err) {
+ next(); // alternatively: res.status(404).send("Cannot GET " + req.path);
+ } else {
+ /*
+ * HACK: Express may still modify our response, turning our 200 status code into a 304
+ * and adding an Etag, unless we ALSO change the req.method from "GET" to something else.
+ * Supposedly, we could also use app.disable('etag'), but I'm not sure that would prevent
+ * Express from changing the status code, and I'm tired of testing work-arounds for this
+ * irritating behavior in Safari.
+ */
+ req.method = "NONE";
+ res.set("Content-Type", "application/xml");
+ res.status(200).send(sData);
+ }
+ });
+ return;
+ }
+ }
+ }
+{% endhighlight %}
+
+I should add that this problem wasn't limited to XML files. It's a problem for the first resource requested by
+Safari for any URL on the site (eg, URLs that default to "index.html" files).
+
+I'm also rather surprised that no one yet seems to have figured out exactly what's going on here between Node+Express
+and Safari. Or maybe they have, and I haven't been keeping my Node.js config up-to-date. I've tried to avoid changing
+too many variables.
+
+Lots of people have run into this problem. For example, on
+[StackOverflow](http://stackoverflow.com/questions/18811286/nodejs-express-cache-and-304-status-code), someone
+concluded that the [node-fresh](https://github.com/visionmedia/node-fresh) module should be changed. And for a while,
+it was changed, until the change was [reverted](https://github.com/visionmedia/node-fresh/issues/8) -- along with a
+lengthy discussion about why the change was wrong and that this was really a bug in Safari.
+
+This "blank page" behavior may well be a bug in Safari, but that doesn't mean Express server components can't
+or shouldn't provide a work-around for that behavior in the meantime. It also seems that some people who decided
+this was a bug in Safari did not actually reproduce the bug themselves.
+
+I don't know the right answer, but I do know that the current situation adversely affects users of other Node-powered
+websites, who will probably get blank pages when they shouldn't, and other developers, who must all discover/debug/work-around
+this problem on their own.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*April 14, 2014*
diff --git a/_posts/2014-04-30-heading-to-new-york.md b/_posts/2014-04-30-heading-to-new-york.md
new file mode 100644
index 000000000..b76da214a
--- /dev/null
+++ b/_posts/2014-04-30-heading-to-new-york.md
@@ -0,0 +1,45 @@
+---
+layout: post
+title: Heading to New York
+date: 2014-04-30 11:00:00
+category: JavaScript
+permalink: /blog/2014/04/30/
+---
+
+Lots of tinkering has been going on here at pcjs.org the past couple of weeks, but with nothing substantial to show for it.
+Fixing lots of little problems requires only small bits of time, whereas buckling down and tackling "the next BIG thing" for
+PCjs requires a much more serious time commitment, with limited interruptions.
+
+And I certainly can't start on "the next BIG thing" now, because I'm heading to New York tomorrow, for [EmpireJS](http://2014.empirejs.org),
+my first JavaScript conference *and* my first trip to New York in about 20 years.
+
+I'm going to the conference partly because it was a great excuse to finally visit New York again (and with the whole family,
+since all three of us are computer nerds), but also to get a more up-close-and-personal sense of where this whole JavaScript
+renaissance is headed.
+
+Is it headed for a fiery crash? This recent blog post
+("[you have ruined javascript](http://codeofrob.com/entries/you-have-ruined-javascript.html)")
+suggests it already has for some people. Is it drowning in the Sea of Endless Proliferation, as this still-relevant two-year-old
+[parody](http://www.webmonkey.com/2012/05/jokes-for-nerds-html9-responsive-boilerstrap-js/) implies? Excerpt:
+
+> *If you’re feeling overwhelmed by the endless proliferation of responsive grids, adaptive images, HTML boilerplates,
+CSS frameworks and JavaScript whirligigs then what you need is the HTML9 Responsive Boilerstrap JS.*
+
+> *To install HTML9 Responsive Boilerstrap JS just “attackclone the grit repo pushmerge, then rubygem the lymphnode js shawarma
+module — and presto!”*
+
+> *If you’re wondering what H9RBS.js actually is, well, you can abandon any hopes of one day being hip. But if you must know,
+H9RBS.js is a “flexible, dependency-free, lightweight, device-agnostic, modular, baked-in, component framework MVC library
+shoelacestrap to help you kickstart your responsive CSS-based app architecture backbone kitchensink tweetybirds.”*
+
+Seriously, I'm concerned about how the JavaScript language (rather than the endless procession of frameworks) is going to evolve,
+and whether it even can. Two examples: at a high-level, we have Microsoft pushing [TypeScript](http://www.typescriptlang.org/),
+and at a much lower level, we have Mozilla pushing [asm.js](http://asmjs.org). And while I like aspects of both those efforts,
+I'm relunctant to go very far down either of those paths right now, because I don't want to get suckered. Inevitably, someone will
+see a *different* shiny object along another fork in the road, and everyone will chase after that instead.
+
+Will [EmpireJS](http://2014.empirejs.org) answer any of these big questions? It doesn't really matter. I just expect to learn stuff,
+and learning is fun!
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*April 30, 2014*
diff --git a/_posts/2014-05-12-chrome-kicks-butt.md b/_posts/2014-05-12-chrome-kicks-butt.md
new file mode 100644
index 000000000..ce7c6727d
--- /dev/null
+++ b/_posts/2014-05-12-chrome-kicks-butt.md
@@ -0,0 +1,33 @@
+---
+layout: post
+title: Chrome Kicks Butt
+date: 2014-05-12 11:00:00
+category: JavaScript
+permalink: /blog/2014/05/12/
+---
+
+I haven't been closely monitoring the performance of PCjs across various browsers. Most of my browser testing has
+been limited to "Does the latest version still work in all current web browsers?"
+
+However, at some point during the last couple months, Chrome's performance suddenly jumped through the roof. On my
+2.8GHz Intel Core i7 MacBook Pro, Chrome v34.0.1847.131 easily punches through the 120Mhz barrier on a PCjs machine
+running PC-DOS 2.00.
+
+That's roughly a 3x-4x increase over previous versions of Chrome. Safari used to be the performance champ, capable
+of running a PCjs machine at a top speed of around 70-80MHz, while Chrome was less than half that, and Firefox was
+slower still.
+
+Chrome has now leap-frogged Safari in a big way, almost doubling Safari's speed. And with Chrome's superior
+Developer Tools, Chrome is clearly the "Browser of Choice," whether you're just playing with PCjs or actually
+debugging it.
+
+Firefox is a bit of a disappointment, given all the hoopla over [asm.js](http://asmjs.org/) and other investments
+that Mozilla is making. I've taken a "wait-and-see" attitude toward asm.js, because PCjs is hand-coded JavaScript,
+and I'm not prepared to build a preprocessor that converts the code to asm.js semantics solely for the benefit of a
+single browser.
+
+Chrome's approach to making regular JavaScript run faster seems to be a winning strategy so far, at least for apps
+like PCjs.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*May 12, 2014*
diff --git a/_posts/2014-06-14-halt-and-catch-liar.md b/_posts/2014-06-14-halt-and-catch-liar.md
new file mode 100644
index 000000000..2a33cb5a8
--- /dev/null
+++ b/_posts/2014-06-14-halt-and-catch-liar.md
@@ -0,0 +1,81 @@
+---
+layout: post
+title: Halt and Catch Liar
+date: 2014-06-14 11:00:00
+category: TV Shows
+permalink: /blog/2014/06/14/
+---
+
+I had high hopes for the new AMC series "[Halt and Catch Fire](http://www.amctv.com/shows/halt-and-catch-fire),"
+but it has proven to be an utter disappointment. I think I can suspend my disbelief as well as anyone, but this
+show requires you to completely turn your brain off in order to be believed. I'm also baffled by the show's
+high [IMDb score](http://www.imdb.com/title/tt2543312/), which is currently 8.4 (out of 10). Either AMC has figured
+out how to game the system, or viewers are easily turned on by clichés, like the know-it-all Hot Programmer,
+the self-assured Sales Guy, and the non-plot-advancing sex that they almost instantly engage in.
+
+The premise: ex-IBM Sales Guy waltzes into a fictional computer company, smooth-talks his way into a top
+marketing position without so much as a resumé, and then immediately risks all, including a huge potential lawsuit
+with IBM, because he has dreams of building IBM clones that are "2x fast" at "1/2 price" -- with *handles*!
+
+And the first thing they must do to achieve this dream is clone the IBM PC ROM BIOS, which the show pretends
+was so secret that you couldn't even tell which chips on the IBM PC motherboard contained the ROM. Never mind
+that IBM published the entire ROM BIOS listing in their Technical Reference Manual, which also included system
+diagrams identifying every chip in the machine. I think if you're going to weave facts into your fiction,
+the least you can do is get your facts right.
+
+And centerpiece of this whole conceit -- the cloning of the IBM PC ROM BIOS. What a farce! Check out this
+scene from Episode 2, where Brilliant Engineer looks at Hot Programmer's whiteboard in awe. Apparently, he
+is easily awed, because he did the same thing when Sales Guy wrote "2x fast, 1/2 price" on an earlier whiteboard.
+
+[
](/blog/images/halt-and-catch-liar.jpg)
+
+So if you deconstruct the code on this whiteboard, you quickly notice that while it IS assembly language, it is
+NOT the sort of assembly language you would find in a ROM BIOS, let alone ANYTHING that would leave you in awe.
+
+Here are some excerpts:
+
+ Initialization
+ MOV AX,CX ; set up DS
+ MOV DS,AX
+ MOV SS,AX ; and SS
+ LEA AX,BEGINSTACK
+ MOV SP,AX
+ MOV AL,OUTINT
+ ...
+ TSTART
+ LEA AX,BEGTRACE
+ MOV POINT,AX
+ MOV AL,YES ; TURN TRACE ON
+ MOV TFLAG,AL
+ MOV AL,NO ; NOT WRAPPED
+ MOV WRAP,AL
+ MOV AX,CS ; CONVERT ADDRESS FOR OUTPUT
+ LEA SI,LOADCS
+ CALL HEXPRT
+ MOV AX,100H
+ LEA SI,LOADIP
+ CALL HEXPRT
+ LEA DX,SIGNIN
+ MOV AH,9
+ INT DOSINT
+ LEA DX,OUTPUT
+ MOV AL,OUTPUT (?)
+ MOV AH,25H ; Set Interrupt Vector
+ INT DOSINT ; Have DOS place the interrupt...
+ ...
+
+This is clearly **NOT** code for a PC ROM BIOS, because no PC BIOS would ever issue a DOS interrupt.
+A BIOS is designed to be called *by* DOS, not the other way around.
+
+This turns out to be code largely copied from a file I found online: [PCTRACE.ASM](http://ftpmirror.your.org/pub/misc/dos/RbbsInABoxVol1No2_640/files/007P/PCTRACE.ZIP-contents/PCTRACE.ASM)
+
+Another curiosity is that searching for this resulted in a "[Googlewhack](http://en.wikipedia.org/wiki/Googlewhack)"
+of sorts:
+
+
+
+Technically, a Googlewhack (a two-word search that yields exactly one result) must use two words found in an actual
+dictionary. But dictionaries are so passé.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*June 14, 2014*
diff --git a/_posts/2014-06-26-more-under-the-hood-changes.md b/_posts/2014-06-26-more-under-the-hood-changes.md
new file mode 100644
index 000000000..e6d718aa9
--- /dev/null
+++ b/_posts/2014-06-26-more-under-the-hood-changes.md
@@ -0,0 +1,18 @@
+---
+layout: post
+title: More Under-The-Hood Changes
+date: 2014-06-26 11:00:00
+category: Releases
+permalink: /blog/2014/06/26/
+---
+
+v1.13.7 of PCjs contains a few minor improvements, mostly in terms of rendering video modes a little more
+efficiently. The rest of the changes to the website involved beefing up support for both "software manifests"
+and "document manifests."
+
+To that end, there's a new [/pubs/](/pubs/) directory for old documents, and [/disks/pcx86/](/disks/pcx86/) contains
+more disk images, with more on the way. I have a TON of old diskette images, and it has taken more time to organize
+them and create manifests than I would like.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*June 26, 2014*
diff --git a/_posts/2014-07-30-ega-support.md b/_posts/2014-07-30-ega-support.md
new file mode 100644
index 000000000..2425209e2
--- /dev/null
+++ b/_posts/2014-07-30-ega-support.md
@@ -0,0 +1,48 @@
+---
+layout: post
+title: EGA Support
+date: 2014-07-30 11:00:00
+category: Video
+permalink: /blog/2014/07/30/
+---
+
+PCjs v1.14.0 now includes basic EGA support. It emulates the EGA hardware well enough to pass the IBM EGA BIOS
+diagnostics and run [Windows 1.01](/devices/pcx86/machine/5160/ega/640kb/win101/) in color. Check out our
+[Windows 1.01 "Server Array"](/devices/pcx86/machine/5160/ega/640kb/array/) demo.
+
+[
](/blog/images/win101-array-demo.jpg)
+
+EGA support is added to a **machine.xml** file using two XML elements; eg:
+
+```xml
+
+```
+
+The *model* attribute must be set to "ega" and the *memory* attribute should be set to the amount of memory
+desired on the card; valid memory sizes are:
+
+- 0x10000 (64Kb)
+- 0x20000 (128Kb)
+- 0x40000 (256Kb)
+
+As with the MDA and CGA video cards, the *screenwidth* and *screenheight* attributes specify the size of display
+window, which the browser will then scale up or down, unless a specific overall size has been specified on the
+<machine> element.
+
+The second required XML element is a <rom> element to load the EGA ROM; eg:
+
+```xml
+
+```
+
+The *notify* attribute must match the *id* of the <video> element, so that the Video component can load
+the initial 8x14 and 8x8 fonts from the ROM. Support for dynamic loading of fonts from plane 2 of the EGA's memory
+will be added later; however, current support works well enough to allow switching from 25-line mode to 43-line mode,
+which essentially switches from the 8x14 font to the 8x8 font.
+
+The <video> element also supports a *switches* attribute to specify the type of monitor connected to the EGA;
+this attribute corresponds to the actual switch settings on the EGA card; our default *switches* setting is "0110",
+which selects an Enhanced Color Monitor, enabling the EGA's maximum resolution of 640x350.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*July 30, 2014*
diff --git a/_posts/2014-08-01-pc-tech-journal-1987.md b/_posts/2014-08-01-pc-tech-journal-1987.md
new file mode 100644
index 000000000..2badf5fb9
--- /dev/null
+++ b/_posts/2014-08-01-pc-tech-journal-1987.md
@@ -0,0 +1,32 @@
+---
+layout: post
+title: PC Tech Journal, 1987
+date: 2014-08-01 11:00:00
+category: PC Tech Journal
+permalink: /blog/2014/08/01/
+---
+
+As part of an ongoing effort to make classic PC technical literature more accessible, I just finished
+scanning and posting the 12 issues of [PC Tech Journal](/pubs/pc/magazines/pctj/) from 1987.
+
+
+
+Future PC Tech Journal postings will include:
+
+- Vol. 1, No. 1, July-August 1983
+- Vol. 3, No. 12, December 1985
+- Vol. 4, Nos. 1-12, 1986
+- Vol. 6, Nos. 1-12, 1988
+
+I'm missing the rest of 1983, all of 1984, most of 1985, and all of 1989 (well, through April 1989, which was
+apparently the last issue).
+
+For a nice overview and brief history of PC Tech Journal, check out the [OS/2 Museum](http://www.os2museum.com/wp/?p=2478).
+
+In the meantime, if you have old PC Tech Journal issues that would fill any of the above holes, let me know.
+I'd be happy to buy them, scan them, and recycle them.
+
+Thanks.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*August 1, 2014*
diff --git a/_posts/2014-08-28-supporting-the-80286.md b/_posts/2014-08-28-supporting-the-80286.md
new file mode 100644
index 000000000..d2e9eb5bd
--- /dev/null
+++ b/_posts/2014-08-28-supporting-the-80286.md
@@ -0,0 +1,53 @@
+---
+layout: post
+title: Supporting the 80286
+date: 2014-08-28 11:00:00
+category: 80286
+permalink: /blog/2014/08/28/
+---
+
+The next milestone for PCx86 is complete 80286 emulation. My hope is to have it working by the end of the year.
+
+PCx86 version 1.15.0 is the first step on the path to full 80286 support. It includes changes to the physical
+memory manager and separate real-mode and protected-mode address evaluators. The Debugger supports physical
+addresses (eg, %FE05B is the same as F000:E05B, assuming real-mode operation), along with breakpoint commands that
+stop execution on port input/output operations. And the ChipSet component now contains "infrastructure" (a
+fancy way of saying "partial support") for multiple PICs, DMA controllers, the 8042 keyboard controller (including
+A20 support), and a bit more -- but not much.
+
+One of the challenges is creating a single "universal" version of PCx86 that can adapt itself to different machine
+types without impacting performance. There will not be a **pc8088.js** or a **pc80286.js** or whatever. There will
+only be **pcx86.js**.
+
+Up until now, all PCx86 machine XML files assumed an 8088 CPU with a 20-bit bus and a model 5150 or 5160 motherboard.
+But now, a machine XML file can specify:
+
+```xml
+
+
+
+...
+```
+
+Conventional emulators are usually NOT able to run original BIOS images, or simulate original PC hardware,
+or even run at the same speed as the original PC, making some software difficult or impossible to use. PCx86 takes a
+different approach, by attempting to simulate an entire PC as it originally existed. Which is why a PCx86 simulation
+of an IBM PC does NOT run at whatever speed your modern PC happens to run in V86-mode or whatever speed your
+browser's JavaScript engine tops out at.
+
+No, a PCx86 simulation of a 4.77Mhz IBM PC runs at 4.77Mhz. And a PCx86 simulation of a 6Mhz IBM PC AT will run at
+6Mhz. If you want to run the simulation faster, you have that option, but that's not the default. And I'm not saying
+that PCx86 is *exact* -- exactness is an exercise I'm leaving for another day and/or to other developers who are even
+more obsessive than I am. I'm just saying that original PCs represent the targets that PCx86 is shooting for.
+
+PCx86 1.15.0 can now load and run the IBM 5170 ROM BIOS up to the first 80286-specific opcode, so it's off and running.
+Although "running" isn't quite the right metaphor, because the process of bringing a new machine simulation to
+completion is a *very* long series of baby steps.
+
+Also, in preparation for this new phase, I recently dug up a variety of old [80286 CPU Documentation](/pubs/pc/reference/intel/80286/)
+and posted excerpts. I'm sure none of this information is "new" at this point, but it might have some historical interest.
+
+Enjoy.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*August 28, 2014*
diff --git a/_posts/2014-09-02-minor-fixes-and-additions.md b/_posts/2014-09-02-minor-fixes-and-additions.md
new file mode 100644
index 000000000..4516ee8cf
--- /dev/null
+++ b/_posts/2014-09-02-minor-fixes-and-additions.md
@@ -0,0 +1,32 @@
+---
+layout: post
+title: Minor Fixes and Additions
+date: 2014-09-02 11:00:00
+category: Releases
+permalink: /blog/2014/09/02/
+---
+
+The following fixes were made in PCjs v1.15.1
+
+ 1. Using the "User-defined URL" option when loading a disk image from a 3rd-party server was broken if the URL
+contained certain special characters; that should be fixed now, but be aware that only web servers (ie, URLs
+using the HTTP protocol) are supported. URLs that trigger a redirect may also not work (more testing required).
+ 2. Any errors that occur during the call to either *embedPC()* or *embedC1P()* should be properly displayed on the
+caller's page now.
+ 3. Two embedding helper functions have been added to provide more control over the machine startup
+and shutdown process:
+ + *enableEvents(boolean)*: pass *false* to disable delivery of all page events to all machines on the page,
+ or *true* to re-enable;
+ + *sendEvent(string)*: pass *"init"*, *"show"* or *"exit"* to simulate the corresponding browser event
+ (*onload*, *onpageshow* or *onbeforeunload*, respectively).
+
+If a page calls *enableEvents(false)* before calling *embedPC()*, all machine layouts will be instantiated
+but the machines themselves will not be initialized. When the page is ready, call *enableEvents(true)* to restore
+normal event processing, and if the browser has already sent the *onload* event, then call *sendEvent("init")*
+to manually initialize the machine(s).
+
+These two new functions are designed to assist in testing the starting up, shutting down and restarting of machines,
+by allowing scripts to control the overall process, without requiring use of the browser's back/forward/close controls.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*September 2, 2014*
diff --git a/_posts/2014-09-13-the-ibm-pc-at-alive-and-booting.md b/_posts/2014-09-13-the-ibm-pc-at-alive-and-booting.md
new file mode 100644
index 000000000..49c60b38f
--- /dev/null
+++ b/_posts/2014-09-13-the-ibm-pc-at-alive-and-booting.md
@@ -0,0 +1,71 @@
+---
+layout: post
+title: "The IBM PC AT: Alive and Booting"
+date: 2014-09-13 11:00:00
+category: Releases
+permalink: /blog/2014/09/13/
+---
+
+My first IBM PC AT (Model 5170) [Test Configuration](/devices/pcx86/machine/5170/ega/640kb/rev1/debugger/) finally
+boots to a PC-DOS prompt. The configuration uses the original [IBM Model 5170 ROM BIOS](/devices/pcx86/rom/5170/),
+dated January 10, 1984.
+
+Getting through the BIOS "POST" (Power-On Self Test) diagnostics was like running an obstacle course, with various
+tests derailing the simulation at every turn.
+
+Sometimes the problems were as simple as missing hardware. For example, I knew that the PC AT contained two DMA
+controllers, for a total of 8 DMA channels, but what I didn't know (or had forgotten) is that it also contained 16
+DMA page registers, some of which the BIOS uses as scratch registers. Since DMA page registers are accessed with
+I/O instructions that work identically in both real-mode and protected-mode, they obviously offer some advantages
+over RAM, especially when the BIOS hasn't yet tested all the RAM, or determined how much RAM is installed, or set up
+descriptors that allow the RAM to be accessed from protected-mode.
+
+Aside from the additional DMA Controller, other major new motherboard components on the PC AT included a second
+8259 Interrupt Controller, an 8042 Keyboard ("Kitchen Sink") Controller, and an MC146818 Real-Time Clock/CMOS chip.
+The Keyboard Controller and Real-Time Clock/CMOS components required the most tinkering to pass through the ROM BIOS
+gauntlet.
+
+For example, at one point, the BIOS ("[TEST.21](http://archive.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 202.pdf)")
+reset the keyboard ("[KBD_RESET](http://archive.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 212.pdf)"),
+which unmasked the keyboard IRQ and waited for an interrupt, using a loop where CX was initialized to zero and then
+decremented until either CX wrapped around to zero again *or* an interrupt occurred. The "TEST.21" code then assumed
+that if "KBD_RESET" returned zero in CX, no interrupt had occurred.
+
+Unfortunately, my Keyboard Controller was a bit too fast: it generated an interrupt as soon as the keyboard IRQ was
+unmasked; as a result, CX was never decremented, leaving it at zero.
+
+---
+
+Most of the effort getting to this point involved adding support for 80286 protected-mode. That work is still far
+from complete, but getting through multiple real-mode/protected-mode round trips in the BIOS was an important
+milestone. Some of the work was outside the CPU component, such as A20 support and processor reset via the 8042
+controller. Work inside the CPU component included:
+
+ - New 80286 general-purpose instructions (eg, ENTER, LEAVE, new PUSH SP behavior, etc)
+ - New protected-mode instructions (eg, ARPL, LGDT, LIDT, LAR, LSL, VERR, VERW, etc)
+ - Protected-mode segment loading, addressing, and fault handling
+
+Another "feature" I spent considerable time on was ensuring that 80286 protected-mode support did not adversely
+real-mode performance, so that the PC and PC XT simulations still run (almost) as fast as before. PCjs dynamically
+reconfigures itself according to the requirements of the processor and platform it's emulating.
+
+---
+
+There's still no support for [LOADALL](/pubs/pc/reference/intel/80286/loadall/) or triple-fault resets, nor for
+call gates or task gates, nor for conforming code segments or expand-down data segments. 80286-specific cycle
+counts haven't been incorporated yet, either. The list of remaining 80286 features is long.
+
+And there's plenty of hardware support left to do: I haven't looked at the AT hard drive controller yet (which
+I believe is significantly different from the XT hard drive controller), and the keyboard *barely* works; the 8042
+Keyboard Controller and AT keyboard had a number of features that older PC/XT keyboards did not (like LEDs and
+programmable repeat rate).
+
+And there are plenty of issues to investigate. For example, PC-DOS is picking up the correct RTC time, but not the
+date (PCjs initializes the RTC to the browser's current date/time, unless a hard-coded date/time is specified in the
+machine XML). And diskette I/O seems a bit slow; I'm concerned that the BIOS is spinning its wheels somewhere
+unnecessarily. And even though the test machine is configured with 640Kb of RAM, the BIOS is reporting only 64Kb.
+
+Hmmmm.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*September 13, 2014*
diff --git a/_posts/2014-09-30-pcjs-coding-conventions.md b/_posts/2014-09-30-pcjs-coding-conventions.md
new file mode 100644
index 000000000..445e5c654
--- /dev/null
+++ b/_posts/2014-09-30-pcjs-coding-conventions.md
@@ -0,0 +1,246 @@
+---
+layout: post
+title: PCjs Coding Conventions
+date: 2014-09-30 11:00:00
+category: JavaScript
+permalink: /blog/2014/09/30/
+---
+
+Here are a few highlights of the (evolving) JavaScript coding conventions used in PCjs.
+
+### Tabs vs. Spaces
+
+I've configured my IDE ([WebStorm](http://www.jetbrains.com/webstorm/)) to NEVER use tab characters in .js files
+(spaces only) and to ALWAYS use tab characters in almost every other type of text file. This is largely because
+when a web browser displays a JavaScript file (either in the main window or in the Developer Tools window), tabs
+usually screw up the formatting, which I find annoying when I'm debugging. XML files, on the other hand,
+are usually reformatted by the browser anyway, so in those cases, I opt for smaller files and use real tabs.
+
+Note that most of the JavaScript delivered by a PCjs production server will have been compiled by Google's
+Closure Compiler, which completely eliminates all non-essential whitespace, so this is just a development
+preference, with little to no impact on production files.
+
+Regardless of the choice of tab character however, I almost always use 4-column tab stops, except in legacy .asm
+files, where 8-column tab stops were the norm.
+
+I've noticed that 2-column tab stops have recently become popular, especially in Node projects; NPM, for example,
+will rewrite package.json files, replacing my 4-column spacing with 2-column spacing. I don't fight that trend -- I
+just ignore it.
+
+### Constants
+
+Property names with all UPPER-CASE letters (with optional numbers and/or underscores) represent constants.
+
+I originally adopted this rule in part because it's a popular C language convention, but also because it
+made it easy to write a preprocessing script (see the PCjs Grunt task **prepjs** in /modules/grunts/prepjs/)
+that replaced all such property references with the corresponding property values and then removed the original
+property definitions. Of course, this convention also depended on the properties never being modified *or* enumerated.
+
+I later discovered that Google's Closure Compiler does an excellent job of automatically inlining properties
+that are never modified or enumerated, so the **prepjs** preprocessing script is no longer used, but I've stuck
+with the UPPER-CASE convention.
+
+I don't bother with JSDoc *@const* annotations, because 1) the project contains far too many constants, 2)
+all the constants are already effectively annotated by virtue of being UPPER-CASE, and 3) there is no noticeable
+improvement in the Closure Compiler's inlining capability with the addition of *@const*.
+
+All constants associated with a component are normally attached to the component's constructor; ie, as properties of
+the constructor. If you think of a JavaScript constructor as a "class', then constants attached to the constructor
+can be thought of as "class constants".
+
+For example, the ChipSet component, which manages (among other things) Programmable Interrupt Controllers or PICs,
+*could* define the constant for an EOI command like this:
+
+``` javascript
+ChipSet.EOI = 0x20; // non-specific EOI (end-of-interrupt)
+```
+
+but since the EOI command is actually one of a number Operation Command Words (specifically, OCW2), I include an
+"OCW2_" prefix in the constant name:
+
+``` javascript
+ChipSet.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
+```
+
+and since I also like to group constants that are associated with a particular register or port, and since I don't
+want the ChipSet constructor becoming littered with property constants, I first define a constant object; in this
+case, **PIC_LO**:
+
+``` javascript
+ChipSet.PIC_LO = {};
+ChipSet.PIC_LO.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
+ChipSet.PIC_LO.OCW2_EOI_SPEC = 0x60; // specific EOI
+ChipSet.PIC_LO.OCW2_EOI_ROT = 0xA0; // rotate on non-specific EOI
+ChipSet.PIC_LO.OCW2_EOI_ROTSPEC = 0xE0; // rotate on specific EOI
+```
+
+By using fully-qualified property names for each constant, the code has a more C-like appearance (think *#define*)
+that's also easier to preprocess.
+
+However, I've gradually switched to the more conventional JavaScript object notation for class constants:
+
+``` javascript
+ChipSet.PIC_LO = {
+ OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
+ OCW2_EOI_SPEC: 0x60, // specific EOI
+ OCW2_EOI_ROT: 0xA0, // rotate on non-specific EOI
+ OCW2_EOI_ROTSPEC: 0xE0 // rotate on specific EOI
+};
+```
+
+because, again, the Closure Compiler does an excellent job inlining such constants (or indeed any property that is
+never modified *or* enumerated).
+
+### DEBUG vs. RELEASE
+
+While we're talking about constants, it's important to be aware of constants that are not scoped to
+any particular component.
+
+In [/modules/shared/lib/defines.js](/modules/shared/lib/defines.js), **DEBUG** is set to **TRUE**,
+enabling all debug-only code by default. It is also declared as a *@define* so that the Closure Compiler can
+override it, setting it to **FALSE** and disabling debug-only code.
+
+To ensure that debug-only code is not simply *disabled* but also *removed*, the code should be wrapped with:
+
+``` javascript
+if (DEBUG) {
+ [code to be removed by the Closure Compiler]
+}
+```
+
+In many cases, the compiler is able to completely remove calls to debug-only class methods; eg:
+
+``` javascript
+Component.assert(off >= 0 && off < this.cb);
+```
+
+However, calls to debug-only instance methods seem to be more problematic, so all such calls are wrapped; eg:
+
+``` javascript
+if (DEBUG) this.log('load("' + sFileURL + '")');
+```
+
+There are a number of other important shared constants in [/modules/shared/lib/defines.js](/modules/shared/lib/defines.js)
+and PCjs-specific constants in [/modules/pcx86/lib/defines.js](/modules/pcx86/lib/defines.js); refer
+to those files for more information.
+
+### Braces and Parentheses
+
+Most opening braces appear at the end of the line containing the associated "if", "while", "for", "switch",
+"function", etc, preceded by a single space. And most opening parentheses are also preceded by a single space,
+except when following "function" or a function name, in which case there is NO space.
+
+There's always the occasional exception. For example, the opening brace of all the top-level (documented)
+functions in a module may appear on its own line, because the extra whitespace can make the code a bit more
+readable.
+
+It's also important to be aware of JavaScript's automatic semicolon insertion feature and the associated danger of
+putting an opening brace below a *return* statement that wants to return an object literal. As long as you (and
+your IDE) are aware of that specific danger, there's no need to be dogmatic about opening braces.
+
+### Variable Names
+
+I still tend to follow Charles Simonyi's "[Hungarian](http://en.wikipedia.org/wiki/Hungarian_notation)" naming
+conventions -- or rather, a naming convention loosely inspired by Hungarian.
+
+For example, if I need a string or numeric variable representing a "thing," I will name it "sThing" if it's a
+string or "iThing" if it's a number (or possibly "nThings" if it represents a total of Things or "cThings"
+if it's a counter of Things). If a string or numeric variable has a very short-term use, I'll probably just name
+it "s" or "i".
+
+As I mention [below](./#quotation-marks), I still tend to distinguish single characters from strings too,
+which means I may sometimes prefix character variables with "ch" and character counters with "cch".
+
+Of course, variable name prefixes like "s" and "n" are irrelevant if you've already given your variables meaningful
+names like "nameOfPerson" or "numberOfPeople". And that's fine -- I sometimes do that as well. But in general,
+I still prefer variable names like "sPerson" and "nPeople".
+
+I don't try to come up with special prefixes for Objects. If there's a Person object, for example, I'll probably
+use colloquial names like "personHere" or "personThere". I am stricter with Arrays though: I prefix array variables
+with "a", arrays of strings and numbers with "as" and "ai" (or "an"), arrays of arrays with "aa", etc. As for Arrays
+of anything else, I usually don't bother with anything more than an "a" prefix.
+
+### Quotation Marks
+
+Because of my C background, I prefer to use double-quotes around multi-character strings and single quotes
+around single-character strings. While the reasons for doing so are largely historical and currently irrelevant,
+characters are STILL the building blocks of strings, and even the JavaScript String class contains methods that
+deal with individual characters (eg, charCodeAt() and fromCharCode()). So for any code that deals explicitly with
+individual characters, I like to reinforce that with single quotes.
+
+Also, to emphasize that object property names aren't really strings (even though strings can be used as property
+names), I tend to use single quotes when quoting property names. That does make me somewhat inconsistent with
+the JSON standard, which insists that property names be double-quoted, but JSON.stringify() takes care of that, so
+it's not really a problem. Besides, I have a lot of quibbles with the JSON standard, like its "disapproval" of
+comments and hexadecimal constants, and its failure to faithfully serialize and deserialize uninitialized Array
+objects, but I'll leave my gripes about JSON for another post.
+
+Generally speaking, the only time I quote property names is when I have to. I'll use the "dot" syntax; eg:
+
+``` javascript
+obj.prop = true;
+```
+
+instead of:
+
+``` javascript
+obj['prop'] = true;
+```
+
+unless the property name doesn't conform to variable name syntax (eg, if it starts with a digit) or if it's a
+"public" property and therefore I can't risk Google's Closure Compiler "minifying" the property name to something
+else.
+
+I break my own quoting rules slightly when dealing with strings that *contain* double-quotes, since it's more readable
+to put double-quotes inside single-quoted strings than to "escape" every double-quote with a backslash.
+
+For code that I originally wrote in PHP and later ported to JavaScript, there was a tendency in the original
+code to always use double-quotes around strings and "escape" double-quotes regardless, and that tendency may linger
+in code I didn't feel like rewriting much, but the tendency was due more to idiosyncrasies of PHP than any convention
+of mine; for example:
+
+- single-quoted PHP strings may not include any escaped characters (except for single-quote and backslash)
+- single-quoted PHP strings cannot resolve references to string variables (eg, "the value of foo is {$foo}")
+
+Because of PHP's restrictions on single-quoted strings, I tended to avoid them. However, in JavaScript, those
+restrictions/features don't exist.
+
+### JSDoc
+
+Most of the PCjs code is documented with [JSDoc](http://usejsdoc.org/) annotations -- not
+because I want to be able to generate documentation (although that's something to think about), but because
+it's the only way to tell both the Closure Compiler and my IDE exactly what data types are passed around.
+The goals are to minimize the number of "code inspection" warnings in the IDE and produce warning-free
+compilations.
+
+In order to use the Closure Compiler's ADVANCED_OPTIMIZATIONS option and get maximum performance (and maximum
+"minification", a form of "uglification"), every function and its parameters needs to be fully typed; otherwise,
+the Compiler generates way too many warnings/errors -- at least, that was the case when I first started using
+it a couple of years ago.
+
+I've adopted a zero-tolerance policy for warnings: nothing gets checked in if the Closure Compiler generates even
+a single warning.
+
+And finally, speaking of warnings, I've had to tell [WebStorm](http://www.jetbrains.com/webstorm/) to "shut up"
+about a few:
+
+- Unfiltered for…in loop
+- Bitwise operator usage
+- Comma expressions
+- loop statement that doesn't loop
+- “throw” of exception caught locally
+
+I acknowledge those those features can introduce bugs if you're not careful, so I make sure I'm careful. I don't
+subscribe to the dogmatic approach that others (eg, the author of JSLint) take about so-called "risky" features.
+I agree that it's always a good idea to walk to the crosswalk before crossing a street, but I don't agree that it's
+*never* a good idea to cross in the middle sometimes, too.
+
+I've also made the following "weak warnings" instead of "warnings":
+
+- Unused JavaScript / ActionScript local symbol
+
+because it's a useful warning, but I don't like being penalized for functions that have been "prototyped" a specific
+way but can't always be implemented exactly as prototyped.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*September 30, 2014*
diff --git a/_posts/2014-10-12-pcjs-released-on-github.md b/_posts/2014-10-12-pcjs-released-on-github.md
new file mode 100644
index 000000000..3b65ea232
--- /dev/null
+++ b/_posts/2014-10-12-pcjs-released-on-github.md
@@ -0,0 +1,27 @@
+---
+layout: post
+title: PCjs Released on GitHub
+date: 2014-10-12 11:00:00
+category: Releases
+permalink: /blog/2014/10/12/
+---
+
+I've decided the time has come to make the [PCjs Project](https://github.com/jeffpar/pcjs) an open source project on
+[GitHub](http://github.com/).
+
+This doesn't mean PCjs is done -- not by a long shot. But I promised to release it on GitHub by the end of
+the year, which is fast approaching, and I didn't really want to do this in December.
+
+I feel I've made pretty good progress on my goals for the year -- primarily EGA and PC AT support. PC AT machines
+can boot and run in real-mode now, but there's still a lot of protected-mode work to do. The big remaining goal for
+this year is to boot OS/2 1.0.
+
+There are also a lot of rough edges left to polish. Holes in EGA emulation remain to be filled (support for dynamically
+loaded fonts is one of the bigger ones). And the PC AT Keyboard, along with the 8042 and Hard Drive controllers, are all
+just limping along at the moment.
+
+Longer term, I'm looking forward to building some new tools, including an "IBM PC Configurator" that will take some
+of the pain out of building your own bootable PCjs machine configurations, and make them easier to share, embed, etc.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 12, 2014*
diff --git a/_posts/2014-10-13-the-8mhz-ibm-pc-at-5170.md b/_posts/2014-10-13-the-8mhz-ibm-pc-at-5170.md
new file mode 100644
index 000000000..96f3adc7f
--- /dev/null
+++ b/_posts/2014-10-13-the-8mhz-ibm-pc-at-5170.md
@@ -0,0 +1,34 @@
+---
+layout: post
+title: The 8Mhz IBM PC AT 5170
+date: 2014-10-13 11:00:00
+category: JavaScript
+permalink: /blog/2014/10/13/
+---
+
+I just added my first [8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/) machine configuration
+to the list of [IBM PC Machine Configurations](/devices/pcx86/machine/), and not surprisingly, the new machine
+fails to boot.
+
+This machine uses the 3rd [ROM BIOS](/devices/pcx86/rom/5170/) that IBM released for the PC AT, a revision that
+included support for 3.5-inch 1.44Mb diskettes -- which will be nice, because I have a number of 1.44Mb diskette
+images I would like to be able to read in a PCjs machine.
+
+Since machines with this BIOS also ran at 8Mhz, I've bumped the CPU speed up to 8,000,000 cycles/second.
+It'll be interesting to see whether this BIOS also increased any of its hard-coded timing delay-loops as a result.
+
+Anyway, when I enabled ChipSet I/O port messages in the Debugger:
+
+ m chipset on
+ m port on
+
+I can see that the BIOS Power-On Self Test (POST) progresses nicely until it starts generating lots of port 0x61
+activity:
+
+ chipset.inPort(0x0061,8042_RWREG): 0x30 at F000:05A8
+ chipset.inPort(0x0061,8042_RWREG): 0x20 at F000:05AE
+
+I know what I'm going to be doing this afternoon now.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 13, 2014*
diff --git a/_posts/2014-10-17-improved-support-for-pc-at-machines.md b/_posts/2014-10-17-improved-support-for-pc-at-machines.md
new file mode 100644
index 000000000..b00e09093
--- /dev/null
+++ b/_posts/2014-10-17-improved-support-for-pc-at-machines.md
@@ -0,0 +1,58 @@
+---
+layout: post
+title: Improved support for PC AT machines
+date: 2014-10-17 11:00:00
+category: Releases
+permalink: /blog/2014/10/17/
+---
+
+The [8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/) machine configuration now boots in
+[PCjs v1.15.5](https://github.com/jeffpar/pcjs/releases/tag/v1.15.5), which includes the following fixes:
+
++ The BIOS expects memory refresh to occur roughly every 16us, which I've resolved by tying the state
+of the refresh bit in port 0x61 to bit 6 of the CPU cycle count (see *in8042RWReg()* in [chipset.js](/modules/pcx86/lib/chipset.js));
+the original AT BIOS was satisfied with a refresh bit that merely alternated, whereas the new AT BIOS
+is much more particular about the rate at which that bit changes, since many hard-coded delay-loops have
+now been replaced with code that waits for a specific number of refresh cycles.
+
++ The 8042 Keyboard Controller emulation needed a few more tweaks, mainly with respect to what happens
+when the keyboard's "clock" line is toggled (see *set8042CmdData()* in [chipset.js](/modules/pcx86/lib/chipset.js)).
+
++ The Floppy Drive Controller needed to add support for the "READ ID" command, in order for the BIOS
+"double-stepping" test to work (double-stepping is required on an 80-track drive when attempting to read
+a 40-track diskette).
+
++ The BIOS Diskette Reset function does something odd after resetting the Floppy Drive Controller: it
+issues not one but *four* "SENSE INTERRUPT STATUS" commands to the FDC, and expects each response to
+return an incrementally larger drive number. I found this a bit mystifying, considering that IBM's
+own FDC/HDC "combo card" supports a maximum of *two* diskette drives. But, there's no point arguing
+with a BIOS that's almost 30 years old.
+
++ The BIOS attempts to detect what its authors must have considered a common problem: the user's failure
+to run SETUP after installing a second hard drive. So, when the CMOS reports only one hard drive installed,
+the BIOS probes for a second hard drive anyway, and it does so by simply writing the drive number to the ATC's
+"DRVHD" register and then immediately reading the "STATUS" register, without issuing any intervening command.
+It was an easy fix to *outATCDrvHd()* in [hdc.js](/modules/pcx86/lib/hdc.js), but I was surprised
+to discover that the ATC had this behavior, and now I'm wondering if there are any other I/O operations
+that must immediately update the "STATUS" register.
+
+This PCjs release also fixes a problem reported by a user: if you disable **localStorage** support in your
+browser, previous versions of PCjs would fault. While every browser that supports PCjs also supports
+**localStorage**, I didn't consider what might happen if a user decided to turn it off.
+
+The only downside to turning off **localStorage** is that none of your PCjs machines will be able save/restore
+their state when you leave/return to the page; they will always reboot.
+
+Browser's don't always refer to the **localStorage** feature by its actual name, either. For example, in
+Chrome, the setting that enables/disables **localStorage** is hidden under "Advanced Settings" => "Privacy" =>
+"Content Settings" => "Cookies" => "Allow local data to be set (recommended)". Which is somewhat misleading
+and a little annoying, because **localStorage** is *not* a **cookie**.
+
+PCjs *never* sets any cookies. Cookies are bits of data that your browser saves and then automatically sends
+off to the server every time you make a request. **localStorage** is nothing more than local storage; it is
+*not* automatically sent anywhere. Granted, a JavaScript application could abuse it and send it out just like a
+cookie, but PCjs does *not* do that; the only exception is when PCjs detects a problem, and even then, you must
+first agree to submit your machine's state as part of the bug report.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 17, 2014*
diff --git a/_posts/2014-10-23-improved-pc-dos-700-support.md b/_posts/2014-10-23-improved-pc-dos-700-support.md
new file mode 100644
index 000000000..50352dfcb
--- /dev/null
+++ b/_posts/2014-10-23-improved-pc-dos-700-support.md
@@ -0,0 +1,27 @@
+---
+layout: post
+title: Improved PC-DOS 7.00 Support
+date: 2014-10-23 11:00:00
+category: Releases
+permalink: /blog/2014/10/23/
+---
+
+[PCjs v1.15.6](https://github.com/jeffpar/pcjs/releases/tag/v1.15.6) is a fairly minor update that fixes a few
+Floppy Drive Controller (FDC) issues and one CPU emulation bug that prevented [PC-DOS 7.00](/disks/pcx86/dos/ibm/7.00/)
+from working properly.
+
+There are also some Debugger improvements; for example, if you turn on "fdc" and "int" messages in the
+Debugger using the "m fdc on" and "m int on" commands, all FDC (INT 0x13) software interrupts will be logged,
+including descriptions and register values.
+
+PC-DOS 7.00 still can't be setup from its specially-formatted 1.84Mb
+[XDF](http://www.os2museum.com/wp/the-xdf-diskette-format/) distribution disk images, "PC-DOS 7.00 (Disk 2)"
+through "PC-DOS 7.00 (Disk 5)", so your best bet is to boot from the 1.44Mb "PC-DOS 7.00 (1.44M Boot)".
+
+Note that you must also use a fairly new 80286 machine configuration, like this
+[8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/),
+in order to use 1.44Mb diskette images; previous models did not support 3.5-inch diskette drives, unless they had been
+retrofitted with a newer [BIOS](/devices/pcx86/rom/5170/).
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 23, 2014*
diff --git a/_posts/2014-10-26-javascript-negativity.md b/_posts/2014-10-26-javascript-negativity.md
new file mode 100644
index 000000000..04744b98e
--- /dev/null
+++ b/_posts/2014-10-26-javascript-negativity.md
@@ -0,0 +1,83 @@
+---
+layout: post
+title: JavaScript Negativity
+date: 2014-10-26 11:00:00
+category: JavaScript
+permalink: /blog/2014/10/26/
+---
+
+Coming from the C programming language, it's easy to be "negative" about how JavaScript deals with 32-bit integers.
+
+As a newcomer, you quickly learn that JavaScript supports only one numeric data type -- 64-bit floats -- and you groan.
+
+Then you learn that all the "bitwise" operators (**~**, **|**, **&**, **^**, **<<**, **>>** and
+**>>>**) treat their operands as 32-bit integer values and produce 32-bit integer results, and you breathe
+a sigh of relief.
+
+But then you start noticing oddities. In C, you can take any 32-bit value, such as -1526726656 (which is equivalent
+to 0xA5000000), mask it with 0x80808080, and get 0x80000000. However, in JavaScript, you actually get -0x80000000,
+which, sadly, is not equal to 0x80000000.
+
+To verify, type the following into any JavaScript REPL (eg, Node):
+
+ > n = -1526726656
+ -1526726656
+ > n &= 0x80808080
+ -2147483648
+ > n == 0x80000000
+ false
+ > n == -0x80000000
+ true
+
+The sign (bit 31) of every 32-bit result is always extended into the entire 52 "significand" bits of the underlying
+64-bit float. And it's impossible to simply "mask away" those additional sign bits, thanks to a fundamental
+restriction of JavaScript bitwise operators: they operate *only* on the low 32 bits.
+
+With one exception: the unsigned right-shift operator. It does more than simply shift zero bits in from
+the left; it also zeros all the bits above the sign bit. This means that `n >>> 0`, while leaving the low 32 bits
+unchanged, also clears the upper bits, resulting in a value that is positive, albeit outside the signed 32-bit range.
+It is equivalent to adding the 33-bit value 0x100000000 to a negative 32-bit number:
+
+ > n = (n < 0? n + 0x100000000 : n)
+ 2147483648
+ > n.toString(16)
+ '80000000'
+
+These operations work because JavaScript is perfectly capable of representing 0x80000000, or any other 32-bit value,
+as a positive number, but it must use a floating point value to do so. And be careful, because as soon as you perform
+*any* bitwise operation on a value with bit 31 set, even an operation as innocuous-looking as:
+
+ > n |= 0
+ -2147483648
+ > n.toString(16)
+ '-80000000'
+
+the result will be negative again. This is simply how all bitwise operators (except for unsigned right-shift) operate:
+they truncate the result to a signed 32-bit value.
+
+This might tempt you to think that the right way to write negative 32-bit constants in hex is to simply precede
+them with a minus sign. But that would be wrong. For example, if you wrote the constant 0x80000080 as "-0x80000080",
+JavaScript would treat that as negation of 2147483776, resulting in a value whose low 32 bits are 0x7FFFFF80, not
+0x80000080.
+
+The safest way to write a 32-bit constant like 0x80000080 is "0x80000080|0", which will produce -2147483520. If you
+write all your negative 32-bit constants that way, then you won't have to resort to using either unsigned right-shifts
+or 33-bit addition, which in turn avoids the use of floating point values.
+
+To continue the fun, try setting bit 0 of 0x80000000, which should give you 0x80000001:
+
+ > n |= 1
+ -2147483647
+ > n.toString(16)
+ '-7fffffff'
+
+WTF? Have all the low 32 bits flipped instead?
+
+Actually, no, this time, I'm pulling your leg. The low 32 bits of the internal value are exactly what you would
+expect: 0x80000001 (the internal representation is more like 0xFFFFF80000001). But as the
+[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)
+explain, for a negative number, toString() returns the positive representation of the number, preceded by a - sign,
+*not* the "two's complement" of the number.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 26, 2014 (Updated September 8, 2015)*
diff --git a/_posts/2014-10-28-limited-support-for-xdf-disk-images.md b/_posts/2014-10-28-limited-support-for-xdf-disk-images.md
new file mode 100644
index 000000000..770ca4126
--- /dev/null
+++ b/_posts/2014-10-28-limited-support-for-xdf-disk-images.md
@@ -0,0 +1,23 @@
+---
+layout: post
+title: Limited Support for XDF Diskettes
+date: 2014-10-28 11:00:00
+category: Releases
+permalink: /blog/2014/10/28/
+---
+
+[PCjs v1.15.7](https://github.com/jeffpar/pcjs/releases/tag/v1.15.7) adds support for the
+[XDF Diskette Format](http://www.os2museum.com/wp/the-xdf-diskette-format/), which was used in
+[PC-DOS 7.00](/disks/pcx86/dos/ibm/7.00/).
+
+However, this support is referred to as "fake" XDF support, because it requires using JSON disk images created
+by DiskDump *without* the experimental "--xdf" option, which is an option that attempts to encode XDF sectors as they
+existed on the original diskettes (ie, with varying lengths and non-standard sector IDs).
+
+"Fake" XDF support works by using conventional 80-track disk images with 23 sectors/track. No standard PC floppy disk
+format ever used 23 sectors/track, but in this case, by distributing the XDF track data across 23 conventional 512-byte
+sectors, the PC-DOS 7.00 Setup code that reads XDF disks succeeds. This was probably one of several fall-back options
+built into the PC-DOS XDF code.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 28, 2014*
diff --git a/_posts/2014-12-04-os2-10.md b/_posts/2014-12-04-os2-10.md
new file mode 100644
index 000000000..7b7ad3565
--- /dev/null
+++ b/_posts/2014-12-04-os2-10.md
@@ -0,0 +1,55 @@
+---
+layout: post
+title: OS/2 1.0
+date: 2014-12-04 11:00:00
+category: OS/2
+permalink: /blog/2014/12/04/
+---
+
+Exciting news for OS/2 fans: PCjs (v1.16.1) is now able to run OS/2 1.0 on
+[IBM PC AT Machine Configurations](/devices/pcx86/machine/#model-5170-machine-configurations). This is the culmination
+of recent work in PCjs to fully emulate the Intel 80286 processor and 16-bit protected-mode, including undocumented
+features like [LOADALL](/pubs/pc/reference/intel/80286/loadall/) and triple-fault resets.
+
+For a quick demo, try the [OS/2 1.0 Debugger Disk](/disks/pcx86/os2/misc/1.0/88286/). In a few seconds,
+you'll see a very rudimentary OS/2 shell (a slimmed-down version of the OS/2 Program Selector) that allows you to
+start the protected-mode command interpreter ("Start a Program") or the real-mode command interpreter ("command.com").
+
+[
](/disks/pcx86/os2/misc/1.0/88286/)
+
+As an added bonus, the Model 5170 machines feature two serial ports, with COM1 connected to a simulated serial
+mouse and COM2 connected to the **Control Panel** output window.
+
+Once you've booted the [OS/2 1.0 Debugger Disk](/disks/pcx86/os2/misc/1.0/88286/) from the assortment of
+[OS/2 Prototype Disks](/disks/pcx86/os2/misc/), you can click on the **Control Panel** output window, press Ctrl-C, and
+find yourself magically transported into the OS/2 Kernel Debugger. The **Control Panel** display is functioning
+as both the output window for all PCjs messages and PCjs Debugger commands, as well as a serial input/output device
+(aka "Dumb Terminal") for any software inside the machine communicating via COM2: in this case, the OS/2 Kernel Debugger.
+
+> SIDEBAR: You can perform similar tricks with DOS in these machines. Boot any DOS disk (version 2.00 and up)
+and type "CTTY COM2" at the DOS prompt. All DOS input/output will now be routed to the **Control Panel** display.
+To restore control to the the machine's keyboard and video display, type "CTTY CON".
+
+Type "?" for a list of all OS/2 Kernel Debugger commands. Type "g" to continue running OS/2. Make sure you type all
+OS/2 Kernel Debugger commands into the **Control Panel** output window. Commands typed into the input box *beneath*
+the output window are processed only by the PCjs Debugger.
+
+When a fault occurs, OS/2 normally displays a "TRAP" message; however, when the Kernel Debugger is running, it
+intercepts the fault and displays the faulting instruction. But the PCjs Debugger has ultimate control: using
+the "m fault on" and "m halt on" commands, the PCjs Debugger will display and halt on any fault first. If you want
+PCjs to deliver the fault to OS/2, single-step over the faulting instruction and then continue.
+
+There are still a number of known issues running OS/2. For example, when attempting to install OS/2 1.0 from the
+installation diskette images onto a hard disk image, OS/2 successfully formats the hard disk and copies the files from
+the first two diskettes, but usually while copying files from either the second or third diskette, the process stops.
+There's no crash -- it simply stops copying files and never finishes. My best guess at this point is that some
+interrupts are being dropped.
+
+Similarly, if a machine running OS/2 1.0 is left unattended for a few minutes, it may stop responding. Again, there's
+no crash or other indication of a problem. The machine simply appears hung. "Ctrl-Alt-Del" and "Reset" buttons still
+work.
+
+The journey continues.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*December 4, 2014*
diff --git a/_posts/2014-12-05-canvas-performance-and-contenteditable.md b/_posts/2014-12-05-canvas-performance-and-contenteditable.md
new file mode 100644
index 000000000..f4df0f7ff
--- /dev/null
+++ b/_posts/2014-12-05-canvas-performance-and-contenteditable.md
@@ -0,0 +1,71 @@
+---
+layout: post
+title: Canvas Performance and ContentEditable
+date: 2014-12-05 11:00:00
+category: HTML5
+permalink: /blog/2014/12/05/
+---
+
+From the beginning of the [JavaScript Machines](/docs/about/) Project, I've always used an HTML5
+[Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) object for both machine output
+and input. It's the obvious choice for output, because the Canvas provides a 2D drawing API that's
+essential both for drawing bitmappped graphics and for faithfully rendering individual characters
+using the machine's original bitmapped fonts.
+
+The Canvas is perhaps a less obvious choice for input, but the theory was that by adding a
+"[contenteditable](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_Editable)" attribute
+to the Canvas object, the user could simply click (or tap) the Canvas to give it focus, and then all the
+usual *onkeydown*, *onkeyup*, and *onkeypress* event handlers would work as expected. The advantage of
+this approach is that it eliminated the need for another on-screen control that would no serve no visual
+purpose.
+
+The "contenteditable" attribute had some issues, but mainly only on mobile devices, so I left those
+issues for another day. For example, using PCjs on an Android device is problematic, in part because
+it doesn't honor the "contenteditable" attribute on a Canvas, but also because Android's built-in
+"soft keyboard" doesn't deliver any keys to the application until you press Enter. So for now, you
+have to use PCjs machines that come with their own "soft keyboard".
+
+However, today I noticed an oddity with Safari on the desktop. For the most part, Safari and Chrome
+perform comparably, and are generally the best browsers to use with PCjs. Firefox used to be a great
+option a couple years ago, but ever since Mozilla started focusing heavily -- perhaps *too* heavily -- on
+[asm.js](http://asmjs.org/) performance, they seem to have fallen behind in overall performance.
+
+But I digress. What I noticed in Safari was that text-scrolling in both DOS and OS/2 was significantly
+slower than Chrome. This seemed very odd -- they should have been almost equally fast. Then I made
+an important discovery: while the machine was scrolling, if I clicked on some other part of the page,
+taking focus *away* from the Canvas, scrolling dramatically sped up. When I clicked on the Canvas
+again, it slowed way down again.
+
+Long story short: when I removed the "contenteditable" attribute from the Canvas, drawing performance
+was consistently fast. The only problem, of course, is that I couldn't type anything into the machine.
+
+So I resurrected some old code I'd written that creates a transparent
+<[textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)> on top of the Canvas,
+and now I use the <textarea> to provide all keyboard, mouse, and touch events (and pointer locking,
+for the handful of browsers that support it).
+
+That seemed to work well, until I tested Safari on an iPad, where I noticed a blinking cursor in the top
+left corner of the machine's screen; that is, the top left corner of the transparent <textarea>. I tried
+all sorts of work-arounds suggested online -- setting the textarea's "color" attribute to "transparent",
+on the theory that the cursor used the same color, or setting the "cursor" attribute to "none" -- but none
+of those work-arounds seemed to, um, work.
+
+I had almost settled on adding iOS detection code, and reverting to the old Canvas input code for iOS only,
+when I noticed that even a Canvas on iOS displayed a blinking cursor -- it was just slightly less annoying
+because the cursor was flush with the left edge of the Canvas. More importantly, it was also as tall as
+the full height of the Canvas.
+
+At this point, it seemed clear that iOS was trying to display the cursor based on what it believed the
+line height to be (ie, the full height of the Canvas). So I switched back to the transparent <textarea>
+again, set its "line-height" attribute to zero, and viola: no more blinking cursor.
+
+So that, in a nutshell, is why v1.16.2 of PCjs comes one day after v1.16.1: because I happened to noticed
+that drawing performance in desktop Safari was suffering, and that there was a fairly straightforward solution.
+
+Safari's behavior should probably be considered a bug, as it's probably doing something it shouldn't,
+like trying to account for an "invisible" blinking cursor. Chrome certainly doesn't have this problem,
+so unless I was the only person in the world who used "contenteditable" Canvases, this is probably something
+Safari will want to fix.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*December 5, 2014*
diff --git a/_posts/2015-01-17-pcjs-uncompiled.md b/_posts/2015-01-17-pcjs-uncompiled.md
new file mode 100644
index 000000000..f22c6b315
--- /dev/null
+++ b/_posts/2015-01-17-pcjs-uncompiled.md
@@ -0,0 +1,48 @@
+---
+layout: post
+title: PCx86 Uncompiled
+date: 2015-01-17 11:00:00
+category: Features
+permalink: /blog/2015/01/17/
+machines:
+ - type: pcx86
+ id: at-ega-1152k-rev3
+ debugger: true
+ uncompiled: true
+ config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/machine.xml
+---
+
+Most PCx86 machines on [{{ site.pcjs.domain }}](/) run with a compiled version of PCx86, which is produced
+by running the PCx86 JavaScript source code through Google's Closure Compiler, yielding a smaller (minified)
+version that loads and runs much faster than the original source code.
+
+However, certain features are disabled in the compiled versions, including a new BACKTRACK feature that
+makes it possible to track the contents of memory locations and registers back to their source (eg, to a ROM
+or file location). Once the BACKTRACK feature is finished, it will be folded into the compiled code, but until
+then, the only way to experiment with it is by running the uncompiled code.
+
+To make it easier to launch machines with uncompiled code, a PCx86 machine definition can now set `uncompiled`
+to *true*, overriding the value of `site.pcjs.compiled` in **_config.yml**.
+
+Here's what a typical Markdown file would look like:
+
+{% raw %}
+ ---
+ ...
+ machines:
+ - type: pcx86
+ id: at-ega-1152k-rev3
+ debugger: true
+ uncompiled: true
+ config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/backtrack/machine.xml
+ ---
+ ...
+ {% include machine.html id="at-ega-1152k-rev3" %}
+{% endraw %}
+
+In fact, that's what we've done in the Markdown file you are reading right now.
+
+{% include machine.html id="at-ega-1152k-rev3" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*January 17, 2015 (Updated December 10, 2015 to reflect the new `uncompiled` property)*
diff --git a/_posts/2015-01-28-new-pcjs-control-panel.md b/_posts/2015-01-28-new-pcjs-control-panel.md
new file mode 100644
index 000000000..e93ff21ce
--- /dev/null
+++ b/_posts/2015-01-28-new-pcjs-control-panel.md
@@ -0,0 +1,23 @@
+---
+layout: post
+title: New PCx86 Control Panel
+date: 2015-01-28 11:00:00
+category: Control Panel
+permalink: /blog/2015/01/28/
+machines:
+ - type: pcx86
+ id: at-ega-1152k-rev3
+ debugger: true
+ uncompiled: true
+ config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/backtrack/machine.xml
+---
+
+A new PCx86 Control Panel is under development, featuring a new "Display Panel" that will provide a variety of
+information about the machine, in real-time, and operate more efficiently than previous DOM-based Control Panels.
+
+A preview of the layout is shown below. There's not much to see yet, as this is very much a work-in-progress.
+
+{% include machine.html id="at-ega-1152k-rev3" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*January 28, 2015*
diff --git a/_posts/2015-02-22-compaq-deskpro-386.md b/_posts/2015-02-22-compaq-deskpro-386.md
new file mode 100644
index 000000000..be644ef22
--- /dev/null
+++ b/_posts/2015-02-22-compaq-deskpro-386.md
@@ -0,0 +1,30 @@
+---
+layout: post
+title: COMPAQ DeskPro 386
+date: 2015-02-22 11:00:00
+category: 80386
+permalink: /blog/2015/02/22/
+machines:
+ - type: pcx86
+ id: deskpro386
+ debugger: true
+ uncompiled: true
+ config: /devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/machine.xml
+---
+
+I finally dumped the [COMPAQ DeskPro 386/16 ROMs](/devices/pcx86/rom/compaq/deskpro386/) from the motherboard I bought
+on ebay last year, so I'm ready to begin adding 80386 support to PCx86.
+
+I'd also like to locate a copy of the "COMPAQ DeskPro 386 Technical Reference Guide, Volumes 1 and 2". It's not hard
+to find COMPAQ Maintenance and Service guides online, but their Technical Reference guides are much rarer, perhaps because
+they were expensive ($149) and not many were sold. Anyway, I'm hoping to either borrow or buy a copy, and then scan and
+post it.
+
+A [COMPAQ DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/) test configuration is displayed below.
+The configuration doesn't run, and the debugger can't disassemble 80386-specific code yet, but this is what I will be
+using to test and debug my changes over the next few months.
+
+{% include machine.html id="deskpro386" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*February 22, 2015*
diff --git a/_posts/2015-02-23-early-80386-cpus.md b/_posts/2015-02-23-early-80386-cpus.md
new file mode 100644
index 000000000..ae5dc2e97
--- /dev/null
+++ b/_posts/2015-02-23-early-80386-cpus.md
@@ -0,0 +1,116 @@
+---
+layout: post
+title: Early 80386 CPUs
+date: 2015-02-23 11:00:00
+category: 80386
+permalink: /blog/2015/02/23/
+---
+
+Assembling a detailed and accurate history of the Intel 80386 CPU, including a complete listing of all the
+"steppings" (revisions), when they were released, what "errata" (problems) each stepping suffered from, and
+which of those problems were fixed by a later stepping, seems virtually impossible at this late date.
+
+See [Intel 80386 CPU Information](/pubs/pc/reference/intel/80386/) for what the PCjs Project has been able to
+collect about 80386 steppings so far, including how some of them were externally marked and internally identified,
+along with lists of associated errata, much of it based on Intel's own documents.
+
+Of course, there's also an eclectic mix of information about early 80386 processors available from various online
+sources. A few of those are highlighted below.
+
+---
+
+Excerpt from "Inside Track", [PC Magazine, February 24, 1987](https://books.google.com/books?id=phxlBt4dX3oC&lpg=PA67&pg=PA67#v=onepage),
+by John C. Dvorak:
+
+> **80386 Bug Stopper Dept.**: If you buy an 80386 machine, card, or chip, make sure you **get the B1 revision of
+the chip** or something newer (B2, B3, and so on). There are **far too many bugs** in the A1 and A2 versions of the
+chip to be acceptable. Here's what to look for: On the top line of the chip you'll see the designation A80386-16.
+If it says A80386-16ES, then it's an engineering sample and the vendor is a *cheapskate*. The samples have the revision
+number on the top line as A1, A2, or B1. Look no further.
+
+> For the rest of you, look at the second line on the chip. If it's S40344 then you have a B1 chip. S40334 is the
+A2 revision and S40276 is the A1 revision....
+
+---
+
+Excerpt from "Tutor", [PC Magazine, October 15, 1991](https://books.google.com/books?id=tSLe3yMjc-AC&pg=PT438&hl=en&sa=X&ved=0ahUKEwjR9MH4gOHJAhVT0mMKHc4tD0YQ6AEIKjAA#v=onepage),
+by Jeff Prosise:
+
+> You can tell if you have a B0 or B1 Step level 386 by looking at the markings on the chip. If it has the ID number
+S40336 or S40337 stamped on it, then it's a Step B0; if it's marked with S40343, S40344, or S40362, it's a Step B1.
+Some B0 and B1 chips were marked B0 or B1 rather than with an ID number.
+
+---
+
+Excerpt from "[CPU Identification by the Windows Kernel](http://www.geoffchappell.com/studies/windows/km/cpu/index.htm)", by Geoff Chappell:
+
+> Finer identification of 80386 processors is largely academic. Whatever the model or stepping, the 80386 processor
+is unsupported since [Windows NT] version 4.0, and soon causes the bug check UNSUPPORTED_PROCESSOR (0x5D), though not
+without the kernel having worked its way through more tests for defects to identify models and steppings. For any 80386
+processor that passes all tests, the model and stepping leap ahead to 3 and 1. Version 3.51, which was the last to
+support the 80386 (and only then in a single-processor configuration), rejects any 80386 that does not pass all these
+tests.
+
+ Family Model Stepping Test
+ ------ ----- -------- ----
+ 3 0 0 32-bit MUL not reliably correct
+ 3 1 0 supports XBTS instruction
+ 3 1 1 set TF bit (0x0100) in EFLAGS causes Debug exception (interrupt 0x01) only at completion of REP MOVSB
+ 3 3 1
+
+> The particular multiplication that distinguishes model 0 is of 0x81 by 0x0417A000. This same test was used by Microsoft
+at least as far back as Windows 3.10 Enhanced Mode, to advise:
+
+ The Intel 80386 processor in this computer does not reliably execute 32-bit
+ multiply operations. Windows usually works correctly on computers with this
+ problem but may occasionally fail. You may want to replace your 80386 processor.
+ Press any key to continue...
+
+> The instruction whose support is tested for model 1 stepping 0 has opcode bytes 0x0F 0xA6 followed by a Mod R/M byte
+and by whatever more this byte indicates is needed for the operand. This opcode is disassembled as XBTS by Microsoft’s
+DUMPBIN utility from Visual C++, and has been since at least the mid-90s. However, the same opcode was apparently reused
+for the CMPXCHG instruction on some 80486 processors. The confusion seems to have left a lasting mark: Intel’s opcode
+charts leave 0x0F 0xA6 unassigned even now. The specific test performed by the Windows kernel is to load EAX and EDX
+with zero and ECX with 0xFF00. If executing XBTS ECX,EDX does not cause an Invalid Opcode exception and clears ecx to
+zero (which CMPXCHG ECX,EDX would not), then XBTS is deemed to be supported and the processor is model 1 stepping 0.
+This case of 80386 processor also was known to Windows 3.10 Enhanced Mode, and was rejected as fatal:
+
+ Windows may not run correctly with the 80386 processor in this computer.
+
+ Upgrade your 80386 processor or start Windows in standard mode by typing
+ WIN /s at the MS-DOS prompt.
+
+> When string instructions such as MOVSB are repeated because of a REP prefix, each operation is ordinarily interruptible.
+As Intel says (for REP in the [Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference N-Z](http://www.intel.com/design/processor/manuals/253667.pdf)),
+this “allows long string operations to proceed without affecting the interrupt response time of the system.” It ordinarily
+applies also to the Debug exception, such as raised by the processor at the end of executing an instruction for which the TF
+bit is set in the EFLAGS when the instruction started. Programmers may have noticed this in the real world of assembly-language
+debugging. If the debugger actually does implement its trace command as a trace, as opposed to setting an INT 3 breakpoint
+where the instruction is calculated to end, then a two-byte REP MOVSB may take many keystrokes to trace through! That
+model 1 stepping 1 traces through a REP MOVSB without interruption may be helpful when debugging, but it is surely a defect.
+
+---
+
+More examples of problems with early 80386 CPUs are posted in "[The Old New Thing](http://blogs.msdn.com/b/oldnewthing/)"
+blog. Here are some highlights from "[My, what strange NOPs you have!](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx)",
+by Raymond Chen:
+
+> [I]f the instruction following a string operation (such as movs) uses opposite-sized addresses from that in the string
+instruction (for example, if you performed a movs es:[edi], ds:[esi] followed by a mov ax, [bx]) or if the following
+instruction accessed an opposite-sized stack (for example, if you performed a movs es:[edi], ds:[esi] on a 16-bit stack,
+and the next instruction was a push), then the movs instruction would not operate correctly....
+
+> [T]here was one bug that manifested itself in incorrect instruction decoding if a conditional branch instruction
+had just the right sequence of taken/not-taken history, and the branch instruction was followed immediately by a selector load,
+and one of the first two instructions at the destination of the branch was itself a jump, call, or return. The easy workaround:
+Insert a NOP between the branch and the selector load....
+
+> [T]he B1 stepping did not support virtual memory in the first 64KB of memory. Fine, don't use virtual memory there....
+
+> If virtual memory was enabled, if a certain race condition was encountered inside the hardware prefetch, and if you executed
+a floating point coprocessor instruction that accessed memory at an address in the range 0x800000F8 through 0x800000FF,
+then the CPU would end up reading from addresses 0x000000F8 through 0x0000000FF instead. This one was easy to work around:
+Never allocate valid memory at 0x80000xxx.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*February 23, 2015*
diff --git a/_posts/2015-03-26-javascript-idiosyncrasies.md b/_posts/2015-03-26-javascript-idiosyncrasies.md
new file mode 100644
index 000000000..59e692470
--- /dev/null
+++ b/_posts/2015-03-26-javascript-idiosyncrasies.md
@@ -0,0 +1,210 @@
+---
+layout: post
+title: JavaScript Idiosyncrasies
+date: 2015-03-26 11:00:00
+category: JavaScript
+permalink: /blog/2015/03/26/
+---
+
+Time to mention a few JavaScript idiosyncrasies, and how I deal with them.
+
+Also, see my previous posts on [PCjs Coding Conventions](/blog/2014/09/30/) and [JavaScript Negativity](/blog/2014/10/26/).
+
+### Strict Equality
+
+Many JavaScript websites will advise you to *never* use the "==" and "!=" JavaScript operators, because when they compare
+variables containing different data types, JavaScript will coerce one of the operands to a matching type, sometimes in
+unexpected ways. We can thank the early days of JavaScript for this feature, when it was trying to be extraordinarily
+forgiving of sloppy code. I'm not going to list all the odd results that can arise from JavaScript's operand coercion,
+because there are more than enough examples on the web already.
+
+To avoid unexpected type coercion, and thus unexpected matches and/or mismatches, the usual advice is to *always* use
+strict equality operators ("===" and "!==").
+
+I disagree.
+
+In well-written code, the variable data types should always be clear. In fact, the more you're able to
+use [JSDoc](http://developers.google.com/closure/compiler/docs/js-for-compiler) types to declare the data types
+of all your parameters, return values, and other variables, the fewer errors you'll have. As long as you're always
+comparing variables with matching types, there shouldn't be any unexpected coercions.
+
+Obviously, there will be times when a polymorphic variable is required, especially when dealing with APIs that can
+return multiple types. But those should be the exception, not the rule.
+
+Another exception is optional parameters. When I write a method with optional parameters, I generally allow those
+parameters to either be omitted (ie, *undefined*) or set to *null*. Using "==", you can check for either value with
+a single comparison:
+
+``` javascript
+if (parameter == null) { ... }
+```
+
+whereas strict equality requires more work:
+
+``` javascript
+if (parameter === undefined || parameter === null) { ... }
+```
+
+This is one of those times when coercion (of *undefined* to *null*), and the use of "non-strict" operators, is beneficial.
+Here's another:
+
+``` javascript
+if (!b) { ... }
+```
+
+Coercing a value to *boolean* is a popular way of checking for all "falsy" values (ie, *undefined*, *null*,
+0, false, "", NaN, etc). It is shorthand for:
+
+``` javascript
+if (b == false) { ... }
+```
+
+yet I suspect the proponents of strict equality would embrace the former while rejecting the latter.
+
+However, I don't recommend "falsy" checks for optional parameters:
+
+``` javascript
+if (!parameter) { ... }
+```
+
+because often a valid numeric parameter might include 0, or a valid string parameter might include "", so it's better
+to do this:
+
+``` javascript
+if (parameter == null) { ... }
+```
+
+and obviously if *null* is also a acceptable value, then you should definitely use strict equality:
+
+``` javascript
+if (parameter === undefined) { ... }
+```
+
+Problems with type coercion are **NOT** problems caused by a poor choice of operators, so trying to make
+those problems go away by artificially limiting your choice of operators seems like the wrong solution.
+Type coercion problems are, by definition, problems involving mismatched types. Solutions include:
+
+- Avoid comparing variables of different types; or
+- Convert your variables to matching types first; or
+- Use strict equality operators (just don't use them mindlessly)
+
+Explicitly convert variables to a single type whenever possible. For example, I might define a method
+that accepts an optional numeric parameter, with a documented default value when it's omitted. I think it's
+important make that parameter unambiguously numeric as soon as possible; eg:
+
+``` javascript
+/**
+ * foo(n)
+ *
+ * Performs a mathematical operation on n and returns a result.
+ *
+ * @param {number} [n] is an optional parameter (defaults to zero if omitted)
+ * @return {number}
+ */
+function foo(n) {
+ n = n || 0;
+ ...
+}
+```
+
+The expression `n || 0` might seem pointless, because *undefined* and *zero* are equivalent in a "falsy" sense, but
+*undefined* is not a number, and there will be fewer problems downstream if you ensure that n is *always* a number.
+
+### Enumerating Array or Object Properties
+
+When using *for*...*in* loops like this:
+
+``` javascript
+var a = [100, 200, 300];
+for (var i in a) { ... }
+```
+
+the type of variable *i* will be **string** rather than **number**; that is, it will contain "0", "1", and "2" rather
+than 0, 1, and 2. If you then use *i* to set a matching element in another array, that element will not be stored in
+the same (numeric) position as the original array.
+
+One solution is to convert *i* to a **number**:
+
+``` javascript
+parseInt(i, 10);
+```
+
+However, a more elegant solution is to use the unary "+" operator to coerce the **string** to a **number**:
+
+``` javascript
++i;
+```
+
+The same problem arises with objects using numeric properties. And watch out for JavaScript's automatic base
+conversion of numeric properties. For example, when you enumerate the properties of object "o":
+
+``` javascript
+var o = {
+ 0x20: ' ',
+ 0x41: 'A'
+};
+```
+
+you will get the strings "32" and "65", not "0x20" and "0x41". You must quote your property names to prevent
+any conversion; eg:
+
+``` javascript
+var o = {
+ "0x20": ' ',
+ "0x41": 'A'
+};
+```
+
+Numeric properties can always be safely converted using the unary "+" operator, regardless whether they were quoted
+or not.
+
+The unary "+" is a great alternative to parseInt(), but be mindful of their differences. One important difference
+is that parseInt() will stop when it encounters an invalid digit, returning whatever value was parsed up to that point,
+whereas unary "+" conversion will return *NaN* if there are any invalid digits in the string.
+
+### Shift Counts For Bitwise Shifts
+
+It turns out that shifting an integer value by more than 31 bits in either direction may not shift as many bits as
+you'd expect. For example:
+
+``` javascript
+n = 0x10000000;
+n >>>= 33;
+```
+
+will shift n by only *one* bit, not 33 bits, and the result will be 0x08000000, not zero. This is because,
+just like the shift instructions on 32-bit Intel processors, JavaScript converts the shift count to a *mod 32* value
+(in other words, it truncates the shift count to a 5-bit value).
+
+So the above example is equivalent to:
+
+``` javascript
+n >>>= 1;
+```
+
+If you really need larger shift counts to work in a consistent manner, you can perform multiple shifts, where each
+shift count is in the range 0-31. Here's one way to shift a number 33 bits:
+
+``` javascript
+n = (n >>> 31) >>> 2;
+```
+
+Also, it's not quite correct to say that a shift count of zero has *no* effect on a number:
+
+``` javascript
+n = 0x88888888|0; // n is displayed as -2004318072
+n >>>= 0; // n is displayed as 2290649224
+```
+
+It's true that the bottom 32 bits of the number were not changed, but a side-effect of the unsigned shift operator
+is that all the upper sign bits are stripped from the (64-bit) result.
+
+Similarly, as soon as you perform any other bitwise operation on the number, even one that does not modify the low
+32 bits, the upper bits will revert to the sign of the lower 32-bit value:
+
+``` javascript
+n |= 0; // n is displayed as -2004318072 again
+```
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*March 26, 2015*
diff --git a/_posts/2015-04-16-compaq-deskpro-386-update.md b/_posts/2015-04-16-compaq-deskpro-386-update.md
new file mode 100644
index 000000000..7f18051ea
--- /dev/null
+++ b/_posts/2015-04-16-compaq-deskpro-386-update.md
@@ -0,0 +1,154 @@
+---
+layout: post
+title: COMPAQ DeskPro 386 Update
+date: 2015-04-16 11:00:00
+category: 80386
+permalink: /blog/2015/04/16/
+machines:
+ - type: pcx86
+ id: deskpro386
+ debugger: true
+ uncompiled: true
+ config: /devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/machine.xml
+---
+
+PCx86 can now boot the [COMPAQ DeskPro 386/16 ROM BIOS](/devices/pcx86/rom/compaq/deskpro386/).
+
+There's still a problem with the Hard Drive Controller, which I haven't looked into yet,
+but booting from a floppy works.
+
+While working through issues with this ROM BIOS, I created some lightly-annotated
+[source code](/devices/pcx86/rom/compaq/deskpro386/1988-01-28/1988-01-28.asm) that can be re-assembled
+with [NASM](http://www.nasm.us/). The initial process of creating the source code is
+explained [here](/devices/pcx86/rom/compaq/deskpro386/#recreating-rom-source-code).
+
+At the top of the source code, I explain a few important details about ROM addresses that
+are worth recapping here:
+
+> This 32Kb ROM image is ORG'ed at 0x8000, because most of its code is designed to run
+at real-mode addresses F000:8000 through F000:FFFF.
+
+> And even though the 80386 resets with CS:IP set to F000:FFF0, the physical base address
+of CS is set to %FFFF0000, which means the ROM must also be mapped at physical addresses
+%FFFF8000 through %FFFFFFFF.
+
+> Additionally, DeskPro 386 systems mirror this 32Kb ROM at real-mode address F000:0000
+through F000:7FFF. Once again, that region is mirrored at physical addresses %FFFF0000
+through %FFFF7FFFF.
+
+> In other words, both 32Kb halves of the last 64Kb of both the first and last megabyte
+of the 80386's 4Gb address space are physically mapped to this ROM image.
+
+> Finally, the DeskPro 386 has a "RAM Relocation" feature that allows 128Kb of RAM at
+%00FE0000 through %00FFFFFF to be mapped to %000E0000 through %000FFFFF, effectively
+replacing the ROM in the first megabyte with write-protected RAM; the top 64Kb of that
+RAM must first be initialized with the 64Kb at %000F0000 prior to remapping. It's also
+possible to copy external ROMs from %000C0000 through %000EFFFF into the bottom 64Kb of
+that RAM, but this is only done for ROMs known to contain relocatable code; eg, a COMPAQ
+Video Graphics Controller (VGC) Board.
+
+> Every DeskPro 386 system must have a MINIMUM of 1Mb of RAM, of which either 256Kb,
+512Kb, or 640Kb can be physically mapped as conventional memory (at the bottom of the
+first megabyte), with the remainder (either 768Kb, 512Kb, or 384Kb) physically mapped
+to the top of the 16th megabyte (ending at address %00FFFFFF), the last 128Kb of which
+is used by the "RAM Relocation" feature. The remaining memory immediately below that
+128Kb (ie, below %00FE0000) can only be accessed by special system software, such as CEMM.
+
+> COMPAQ refers to that remaining memory as "Compaq Built-in Memory".
+
+So there you have it. Once the ROM has relocated itself to RAM at the top of the 16th
+megabyte, there are no less than THREE physical address ranges where ROM code and data
+structures can be accessed:
+
+ 1. %000F0000 through %000FFFFF (aka real-mode adresses F000:0000 through F000:FFFF)
+ 2. %00FF0000 through %00FFFFFF (the relocated copy)
+ 3. %FFFF0000 through %FFFFFFFF (the physical alias of %000F0000 through %000FFFFF)
+
+As you would expect, most of the ROM's code and data references are to first megabyte,
+since most of the code is designed to run in real-mode. But there are portions that
+run in protected-mode, and those portions are much less consistent about which address
+range to use -- no doubt, in part, because it makes no difference. The ROM does
+not run with paging enabled, so any physical address is as easy to access as any other.
+
+Unless, of course, the A20 line is disabled. In that case, only the first range is
+accessible; the other two are not.
+
+April 19, 2015 Update
+---
+Thanks to some sleuthing by [Michal Necasek](http://os2museum.com/), it turns out that my
+assumptions about A20 management on the COMPAQ DeskPro 386 were incorrect.
+
+He noted that, on page 398 of "DOS Internals" by Geoff Chappell, (c) 1994, the author says:
+
+> On a machine that controls the A20 by passing the address line through an AND gate with a
+signal from some bit at an I/O port, the A20MAP program should produce a map similar to:
+
+ Memory mapping with disabled A20 line:
+
+ 0MB -> 0MB
+ 1MB -> 0MB
+ 2MB -> 2MB
+ 3MB -> 2MB
+ 4MB -> 4MB
+ 5MB -> 4MB
+ 6MB -> 6MB
+ 7MB -> 6MB
+
+> showing wrap-around for every second megabyte. It is also possible to include other address lines
+in the controlling mechanism, which may reduce the incidence of wrap-around, as with a Compaq
+DeskPro:
+
+ 0MB -> 0MB
+ 1MB -> 0MB
+ 2MB -> 2MB
+ 3MB -> 3MB
+ 4MB -> 4MB
+
+---
+
+This means that the DeskPro ROM BIOS can, in fact, access its own code and data at ANY of the above
+three physical address ranges at any time, regardless whether A20 is disabled or not.
+
+Which is a good thing, because I came across at least one code sequence in the ROM BIOS that enters
+protected-mode with A20 disabled -- an unwise thing to do on most machines:
+
+ ;
+ ; When we arrive here, the A20 line has been disabled; on most systems, that would
+ ; mean that the ROM's GDT would only be accessible at the "low" ROM address (%0F0730),
+ ; not the "high" address (%FF0730). But fortunately, A20 management on COMPAQ
+ ; DeskPros affects wrap-around only from the 1st to the 2nd megabyte; no other address
+ ; range is affected.
+ ;
+ ; FYI, it seems this code doesn't do anything if bits 6 and 7 of the RAM Settings
+ ; register are set to anything other than 0x40 (ie, it returns to real-mode almost
+ ; immediately after entering protected-mode).
+ ;
+ lgdt [cs:0x077e] ; 0000F498 2E0F01167E07; load [gdtr_hi] into GDTR
+ mov eax,cr0 ; 0000F49E 0F2000
+ or ax,0x1 ; 0000F4A1 0D0100
+ mov cr0,eax ; 0000F4A4 0F2200
+ jmp 0x28:xf4ac ; 0000F4A7 EAACF42800
+
+Before fully understanding the DeskPro's unusual A20 management, PCx86 worked around it by
+redirecting all A20 changes from the Bus component to the CPU component, giving the CPU first
+crack at any changes to A20. If the CPU was in real-mode, it would simply pass the A20 request
+on to the Bus. However, if the CPU was in protected-mode, it would maintain the requested
+"logical" A20 state but ensure that the "physical" state of A20 was always enabled. In short,
+it was no longer possible for the CPU to be in protected-mode AND for the A20 line to be disabled;
+when one was enabled, the other was enabled as well.
+
+I'm in the process of replacing that work-around with a much more compatible change, at least
+on 32-bit bus configurations, which involves changing the physical address map for the 2nd megabyte
+to match that of the 1st megabyte whenever A20 is disabled. I could probably get away with remapping
+only the first 64Kb of the 2nd megabyte, but until I'm actually able to run some tests on a real
+DeskPro 386, I'm going to assume COMPAQ's A20 implementation affected the entire 2nd megabyte.
+
+Here's my [COMPAQ DeskPro 386/16](/devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/) test
+configuration. Set a breakpoint at F000:F498 ("bp f000:f498") in the Debugger panel to see the above
+code in action. When the machine is operating in real-mode, you can use the "rp" command to dump all
+the registers, including the current base and limit values loaded into the segment registers.
+
+{% include machine.html id="deskpro386" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*April 16-19, 2015*
diff --git a/_posts/2015-05-20-pc-tech-journal-collection.md b/_posts/2015-05-20-pc-tech-journal-collection.md
new file mode 100644
index 000000000..87e50d8d6
--- /dev/null
+++ b/_posts/2015-05-20-pc-tech-journal-collection.md
@@ -0,0 +1,26 @@
+---
+layout: post
+title: PC Tech Journal Collection
+date: 2015-05-20 11:00:00
+category: PC Tech Journal
+permalink: /blog/2015/05/20/
+---
+
+This is an update to my 2014 [post](/blog/2014/08/01/) on the PCjs online collection of old
+[PC Tech Journal](/pubs/pc/magazines/pctj/) magazine issues.
+
+Our collection is much more complete now. We have the first issue, the last issue, and almost all the issues
+in between. All we're currently missing are the April 1988 issue and the first three issues of 1989, at least in
+terms of regular issues.
+
+We also have the [1987 Editorial Index and Comprehensive Product Guide](/pubs/pc/magazines/pctj/PCTJ-1987-00/);
+however, even though it identifies itself as a 1987 issue, the editorial index only covers issues through
+October 1986, so "technically" it should be considered a late 1986 issue. It is officially Vol. 4, No. 13, which,
+numerically, puts it squarely between the December 1986 and January 1987 issues.
+
+[
](/pubs/pc/magazines/pctj/)
+
+Happy reading!
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*May 20, 2015*
diff --git a/_posts/2015-06-01-debugging-the-ibm-vga-rom.md b/_posts/2015-06-01-debugging-the-ibm-vga-rom.md
new file mode 100644
index 000000000..320e00fd4
--- /dev/null
+++ b/_posts/2015-06-01-debugging-the-ibm-vga-rom.md
@@ -0,0 +1,142 @@
+---
+layout: post
+title: Debugging the IBM VGA ROM
+date: 2015-06-01 11:00:00
+category: Video
+permalink: /blog/2015/06/01/
+---
+
+The IBM VGA ("Video Graphics Array") standard was introduced as part of the IBM PS/2 line of computers;
+it was not a feature you could purchase or install in older PC, XT or AT-compatible machines. In fact, full VGA
+support was not even available in all PS/2 models.
+
+Of the first four PS/2 models -- the 8086-based Model 30, the 80286-based Model 50 and Model 60, and the 80386-based
+Model 80 -- VGA support was available only in the three higher-end models. The Model 30 came with MCGA ("Multicolor
+Graphics Array") video hardware that supported a subset of VGA modes (eg, 640x480 2-color and 320x200 256-color graphics).
+
+It wasn't until October 1987 that IBM finally introduced an 8-bit ISA card that brought VGA capability to older PCs.
+The card was called the **IBM PS/2 Display Adapter**. However, I think the name is a bit confusing, since the card
+could only be used in PC, XT, and AT-compatible systems. I'll refer to it here simply as the IBM VGA.
+
+The VGA ROM used here is assumed to have come from an original IBM VGA. It's unknown if IBM ever made any
+revisions to the VGA ROM. With the introduction of the PS/2 family and the VGA, IBM decided to no longer publish
+the source code for its ROMs, so I've created some assemblable source code from the IBM VGA ROM
+[here](/devices/pcx86/video/ibm/vga/).
+
+I've finally started debugging a machine configuration that uses the IBM VGA ROM. Since the VGA and the 80386 are
+contemporaries, I'm using an [80386 machine configuration](/devices/pcx86/machine/compaq/deskpro386/vga/2048kb/debugger/).
+However, I don't expect the IBM VGA ROM to require any 80386 support or PS/2-specific features.
+
+The first problem I ran into was here:
+
+ ;
+ ; Initialize the ROM BIOS Video Mode Options byte @40:0087 (default to color and 256Kb of RAM)
+ ;
+ mov byte [0x487],0x60 ; 0000008D
+ ;
+ ; The x100 subroutine alternately enables port 0x3B? and 0x3D? decoding, verifying that there is
+ ; no response on opposing ports 0x3D? and 0x3B?, respectively; otherwise, it assumes that another
+ ; video card must exist and attempts to select co-existing settings for the VGA. For example, if
+ ; there is an unexpected response on the color ports, the VGA ROM will default to mono operation.
+ ;
+ call x100 ; 00000092
+
+The Video component installs I/O port handlers for all possible I/O ranges; when a range isn't being used, the
+associated I/O operations are redirected to a dummy Card, so that the active Card isn't affected. Here,
+however, that was insufficient. If the VGA is the only installed video card, the VGA ROM expects *NO RESPONSE*
+on inactive CRTC ports. So I've changed the CRTC I/O handlers to check the Card's fActive flag. This seems
+like a safe and logical change, but I still have to check for backward-compatibility issues with older ROMs.
+
+Other problems included:
+
+ * Some bugs in Read Mode 1 that caused a memory test failure
+ * Horizontal and vertical retrace timing issues (the ROM requires a specific number of intervals per second)
+ * Differences between the EGA and VGA in the SWSENSE bit (bit 4) of Input Status Register 0
+
+The last problem was the most puzzling, because the ROM programs a series of values into the first DAC register,
+and expects the SWSENSE bit of Input Status Register 0 to change in very specific ways, depending on the kind
+of monitor attached.
+
+I've not found any hardware documentation that explains exactly how this should work. IBM's own Technical Reference
+material is extremely vague:
+
+ "Bit 4: Switch Sense Bit - This bit allows the system microprocessor to read the switch sense line.
+ This bit allows the power-on self-test to determine if a monochrome or color display is connected to
+ the system."
+
+I've hard-coded a solution that assumes a color monitor. Support for using a monochrome monitor with an EGA was
+never completed, and this is another related issue that will have to be resolved for the VGA as well.
+
+---
+
+While debugging and fixing assorted IBM VGA issues, I made a table of all the register values for the video
+modes commonly used on the IBM VGA. Here's that table:
+
+ INT 0x10 Mode Requested: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x0D 0x0E 0x10 0x12 0x13
+
+ BIOSMODE: 0x01 0x01 0x03 0x03 0x04 0x04 0x06 0x0D 0x0E 0x10 0x12 0x13
+ CRTC[0x00]: HTOTAL 0x2D 0x2D 0x5F 0x5F 0x2D 0x2D 0x5F 0x2D 0x5F 0x5F 0x5F 0x5F
+ CRTC[0x01]: HDISP_END 0x27 0x27 0x4F 0x4F 0x27 0x27 0x4F 0x27 0x4F 0x4F 0x4F 0x4F
+ CRTC[0x02]: HBLANK_START 0x28 0x28 0x50 0x50 0x28 0x28 0x50 0x28 0x50 0x50 0x50 0x50
+ CRTC[0x03]: HBLANK_END 0x90 0x90 0x82 0x82 0x90 0x90 0x82 0x90 0x82 0x82 0x82 0x82
+ CRTC[0x04]: HRETRACE_START 0x2B 0x2B 0x55 0x55 0x2B 0x2B 0x54 0x2B 0x54 0x54 0x54 0x54
+ CRTC[0x05]: HRETRACE_END 0xA0 0xA0 0x81 0x81 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80
+ CRTC[0x06]: VTOTAL 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0x0B 0xBF
+ CRTC[0x07]: OVERFLOW 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x3E 0x1F
+ CRTC[0x08]: PRESET_ROW 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ CRTC[0x09]: MAX_SCAN 0x4F 0x4F 0x4F 0x4F 0xC1 0xC1 0xC1 0xC0 0xC0 0x40 0x40 0x41
+ CRTC[0x0A]: CURSOR_START 0x0D 0x0D 0x0D 0x0D 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ CRTC[0x0B]: CURSOR_END 0x0E 0x0E 0x0E 0x0E 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ CRTC[0x0C]: START_ADDR_HI 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ CRTC[0x0D]: START_ADDR_LO 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ CRTC[0x0E]: CURSOR_ADDR_HI 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00
+ CRTC[0x0F]: CURSOR_ADDR_LO 0x19 0x19 0x41 0x41 0x19 0x19 0x41 0x19 0x41 0x41 0xE1 0xA2
+ CRTC[0x10]: VRETRACE_START 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x83 0xEA 0x9C
+ CRTC[0x11]: VRETRACE_END 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x85 0x8C 0x8E
+ CRTC[0x12]: VDISP_END 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x5D 0xDF 0x8F
+ CRTC[0x13]: OFFSET 0x14 0x14 0x28 0x28 0x14 0x14 0x28 0x14 0x28 0x28 0x28 0x28
+ CRTC[0x14]: UNDERLINE 0x1F 0x1F 0x1F 0x1F 0x00 0x00 0x00 0x00 0x00 0x0F 0x00 0x40
+ CRTC[0x15]: VBLANK_START 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x63 0xE7 0x96
+ CRTC[0x16]: VBLANK_END 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xBA 0x04 0xB9
+ CRTC[0x17]: MODE_CTRL 0xA3 0xA3 0xA3 0xA3 0xA2 0xA2 0xC2 0xE3 0xE3 0xE3 0xE3 0xA3
+ CRTC[0x18]: LINE_COMPARE 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ GRC[0x00]: SRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ GRC[0x01]: ESRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ GRC[0x02]: COLORCMP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ GRC[0x03]: DATAROT 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ GRC[0x04]: READMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ GRC[0x05]: MODE 0x10 0x10 0x10 0x10 0x30 0x30 0x00 0x00 0x00 0x00 0x00 0x40
+ GRC[0x06]: MISC 0x0E 0x0E 0x0E 0x0E 0x0F 0x0F 0x0D 0x05 0x05 0x05 0x05 0x05
+ GRC[0x07]: COLORDC 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0F 0x0F 0x0F 0x0F 0x0F
+ GRC[0x08]: BITMASK 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
+ SEQ[0x00]: RESET 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03
+ SEQ[0x01]: CLOCKING 0x08 0x08 0x00 0x00 0x09 0x09 0x01 0x09 0x01 0x01 0x01 0x01
+ SEQ[0x02]: MAPMASK 0x03 0x03 0x03 0x03 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
+ SEQ[0x03]: CHARMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ SEQ[0x04]: MEMMODE 0x03 0x03 0x03 0x03 0x02 0x02 0x06 0x06 0x06 0x06 0x06 0x0E
+ ATC[0x00]: PAL00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ ATC[0x01]: PAL01 0x01 0x01 0x01 0x01 0x13 0x13 0x17 0x01 0x01 0x01 0x01 0x01
+ ATC[0x02]: PAL02 0x02 0x02 0x02 0x02 0x15 0x15 0x17 0x02 0x02 0x02 0x02 0x02
+ ATC[0x03]: PAL03 0x03 0x03 0x03 0x03 0x17 0x17 0x17 0x03 0x03 0x03 0x03 0x03
+ ATC[0x04]: PAL04 0x04 0x04 0x04 0x04 0x02 0x02 0x17 0x04 0x04 0x04 0x04 0x04
+ ATC[0x05]: PAL05 0x05 0x05 0x05 0x05 0x04 0x04 0x17 0x05 0x05 0x05 0x05 0x05
+ ATC[0x06]: PAL06 0x14 0x14 0x14 0x14 0x06 0x06 0x17 0x06 0x06 0x14 0x14 0x06
+ ATC[0x07]: PAL07 0x07 0x07 0x07 0x07 0x07 0x07 0x17 0x07 0x07 0x07 0x07 0x07
+ ATC[0x08]: PAL08 0x38 0x38 0x38 0x38 0x10 0x10 0x17 0x10 0x10 0x38 0x38 0x08
+ ATC[0x09]: PAL09 0x39 0x39 0x39 0x39 0x11 0x11 0x17 0x11 0x11 0x39 0x39 0x09
+ ATC[0x0A]: PAL0A 0x3A 0x3A 0x3A 0x3A 0x12 0x12 0x17 0x12 0x12 0x3A 0x3A 0x0A
+ ATC[0x0B]: PAL0B 0x3B 0x3B 0x3B 0x3B 0x13 0x13 0x17 0x13 0x13 0x3B 0x3B 0x0B
+ ATC[0x0C]: PAL0C 0x3C 0x3C 0x3C 0x3C 0x14 0x14 0x17 0x14 0x14 0x3C 0x3C 0x0C
+ ATC[0x0D]: PAL0D 0x3D 0x3D 0x3D 0x3D 0x15 0x15 0x17 0x15 0x15 0x3D 0x3D 0x0D
+ ATC[0x0E]: PAL0E 0x3E 0x3E 0x3E 0x3E 0x16 0x16 0x17 0x16 0x16 0x3E 0x3E 0x0E
+ ATC[0x0F]: PAL0F 0x3F 0x3F 0x3F 0x3F 0x17 0x17 0x17 0x17 0x17 0x3F 0x3F 0x0F
+ ATC[0x10]: MODE 0x0C 0x0C 0x0C 0x0C 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x41
+ ATC[0x11]: OVERSCAN 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+ ATC[0x12]: PLANES 0x0F 0x0F 0x0F 0x0F 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
+ ATC[0x13]: HPAN 0x08 0x08 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
+
+In addition, I've created a [VGA Tests](/tests/pc/vga/) directory to hold VGA test and sample code that PCjs can
+now successfully run (for the most part). See that directory for more details.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*June 1, 2015 (Updated July 9, 2015)*
diff --git a/_posts/2015-06-05-the-strange-case-of-the-ega-graphics-scroll-bug.md b/_posts/2015-06-05-the-strange-case-of-the-ega-graphics-scroll-bug.md
new file mode 100644
index 000000000..5aa02cc2f
--- /dev/null
+++ b/_posts/2015-06-05-the-strange-case-of-the-ega-graphics-scroll-bug.md
@@ -0,0 +1,186 @@
+---
+layout: post
+title: The Strange Case of the EGA Graphics Scroll Bug
+date: 2015-06-05 11:00:00
+category: Video
+permalink: /blog/2015/06/05/
+---
+
+I was playing with different video modes using this [IBM PC AT w/EGA](/devices/pcx86/machine/5170/ega/640kb/rev1/debugger/),
+and I discovered an odd problem.
+
+For example, when I ran this code:
+
+ A>b:debug
+ -a
+ 0CE0:0100 mov ax,e
+ 0CE0:0103 int 10
+ 0CE0:0105 int 3
+ 0CE0:0106
+ -g
+
+the following "text" correctly appeared at the top of the screen, in 640x200 16-color graphics mode 0x0E:
+
+ AX=0B01 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
+ DS=0CE0 ES=0CE0 SS=0CE0 CS=0CE0 IP=0105 NV UP EI PL NZ NA PO NC
+ 0CE0:0105 CC INT 3
+ -
+
+And when I typed "q", then "cls" and finally "dir", the screen filled with DOS directory contents.
+
+But as soon as the screen started to scroll, the screen contents became garbled. Other EGA graphics modes,
+like 640x350 16-color mode 0x10, didn't have this problem.
+
+To investigate, I set a breakpoint in the IBM EGA ROM where the scrolling starts, at 0xC000:12EA (see p.130 of the
+"IBM Enhanced Graphics Adapter" Technical Reference document):
+
+ CRANK_A:
+ PUSH CX
+ MOV CL,DL
+ SUB CH,CH
+ PUSH SI
+ PUSH DI
+ REP MOVSB
+ POP DI
+ POP SI
+ ADD SI,BX
+ ADD DI,BX
+ POP CX
+ LOOP CRANK_A
+
+CX contains 0xC0 (192), which is the number of scan-lines to move up, and BX contains 0x50 (80), the number
+of bytes per scan-line.
+
+When the breakpoint was hit, I dumped the video hardware state, using the Debugger's "*d video*" command.
+For comparison purposes, I've pasted the corresponding video state for mode 0x10 on the right-hand side.
+
+ breakpoint hit: C000:12EA (exec)
+ stopped (175707689 ops, 800060264 cycles, 133470 ms, 5994308 hz)
+ AX=020F BX=0050 CX=00C0 DX=1950 SP=0B58 BP=0511 SI=0280 DI=0000
+ SS=011F DS=A000 ES=A000 PS=0246 V0 D0 I1 T0 S0 Z1 A0 P1 C0
+ C000:12EA 51 PUSH CX
+
+ BIOSMODE: 0x0E BIOSMODE: 0x10
+ CRTC[0x00]: HORZ_TOTAL 0x70 CRTC[0x00]: HORZ_TOTAL 0x5B
+ CRTC[0x01]: HORZ_DISP_END 0x4F CRTC[0x01]: HORZ_DISP_END 0x4F
+ CRTC[0x02]: HORZ_BLANK_START 0x59 CRTC[0x02]: HORZ_BLANK_START 0x53
+ CRTC[0x03]: HORZ_BLANK_END 0x2D CRTC[0x03]: HORZ_BLANK_END 0x37
+ CRTC[0x04]: HORZ_RETRACE_START 0x5E CRTC[0x04]: HORZ_RETRACE_START 0x52
+ CRTC[0x05]: HORZ_RETRACE_END 0x06 CRTC[0x05]: HORZ_RETRACE_END 0x00
+ CRTC[0x06]: VERT_TOTAL 0x04 CRTC[0x06]: VERT_TOTAL 0x6C
+ CRTC[0x07]: OVERFLOW 0x11 CRTC[0x07]: OVERFLOW 0x1F
+ CRTC[0x08]: PRESET_ROW_SCAN 0x00 CRTC[0x08]: PRESET_ROW_SCAN 0x00
+ CRTC[0x09]: MAX_SCAN_LINE 0x00 CRTC[0x09]: MAX_SCAN_LINE 0x00
+ CRTC[0x0A]: CURSOR_START 0x00 CRTC[0x0A]: CURSOR_START 0x00
+ CRTC[0x0B]: CURSOR_END 0x01 CRTC[0x0B]: CURSOR_END 0x01
+ CRTC[0x0C]: START_ADDR_HI 0x00 CRTC[0x0C]: START_ADDR_HI 0x00
+ CRTC[0x0D]: START_ADDR_LO 0x00 CRTC[0x0D]: START_ADDR_LO 0x00
+ CRTC[0x0E]: CURSOR_ADDR_HI 0x07 CRTC[0x0E]: CURSOR_ADDR_HI 0x01
+ CRTC[0x0F]: CURSOR_ADDR_LO 0x80* CRTC[0x0F]: CURSOR_ADDR_LO 0x41*
+ CRTC[0x10]: VERT_RETRACE_START 0xE0 CRTC[0x10]: VERT_RETRACE_START 0x5E
+ CRTC[0x11]: VERT_RETRACE_END 0x23 CRTC[0x11]: VERT_RETRACE_END 0x2B
+ CRTC[0x12]: VERT_DISP_END 0xC7 CRTC[0x12]: VERT_DISP_END 0x5D
+ CRTC[0x13]: OFFSET 0x28 CRTC[0x13]: OFFSET 0x28
+ CRTC[0x14]: UNDERLINE 0x00 CRTC[0x14]: UNDERLINE 0x0F
+ CRTC[0x15]: VERT_BLANK_START 0xDF CRTC[0x15]: VERT_BLANK_START 0x5F
+ CRTC[0x16]: VERT_BLANK_END 0xEF CRTC[0x16]: VERT_BLANK_END 0x0A
+ CRTC[0x17]: MODE_CTRL 0xE3 CRTC[0x17]: MODE_CTRL 0xE3
+ CRTC[0x18]: LINE_COMPARE 0xFF CRTC[0x18]: LINE_COMPARE 0xFF
+ STATUS1: 0x01 STATUS1: 0x01
+ ATCDATA: true ATCDATA: true
+ ATC[0x00]: PAL00 0x00 ATC[0x00]: PAL00 0x00
+ ATC[0x01]: PAL01 0x01 ATC[0x01]: PAL01 0x01
+ ATC[0x02]: PAL02 0x02 ATC[0x02]: PAL02 0x02
+ ATC[0x03]: PAL03 0x03 ATC[0x03]: PAL03 0x03
+ ATC[0x04]: PAL04 0x04 ATC[0x04]: PAL04 0x04
+ ATC[0x05]: PAL05 0x05 ATC[0x05]: PAL05 0x05
+ ATC[0x06]: PAL06 0x06 ATC[0x06]: PAL06 0x14
+ ATC[0x07]: PAL07 0x07 ATC[0x07]: PAL07 0x07
+ ATC[0x08]: PAL08 0x10 ATC[0x08]: PAL08 0x38
+ ATC[0x09]: PAL09 0x11 ATC[0x09]: PAL09 0x39
+ ATC[0x0A]: PAL0A 0x12 ATC[0x0A]: PAL0A 0x3A
+ ATC[0x0B]: PAL0B 0x13 ATC[0x0B]: PAL0B 0x3B
+ ATC[0x0C]: PAL0C 0x14 ATC[0x0C]: PAL0C 0x3C
+ ATC[0x0D]: PAL0D 0x15 ATC[0x0D]: PAL0D 0x3D
+ ATC[0x0E]: PAL0E 0x16 ATC[0x0E]: PAL0E 0x3E
+ ATC[0x0F]: PAL0F 0x17 ATC[0x0F]: PAL0F 0x3F
+ ATC[0x10]: MODE 0x01 ATC[0x10]: MODE 0x01
+ ATC[0x11]: OVERSCAN 0x00 ATC[0x11]: OVERSCAN 0x00
+ ATC[0x12]: PLANES 0x0F ATC[0x12]: PLANES 0x0F
+ ATC[0x13]: HORZPAN 0x00 ATC[0x13]: HORZPAN 0x00
+ GRC[0x00]: SRESET 0x00 GRC[0x00]: SRESET 0x00
+ GRC[0x01]: ESRESET 0x00 GRC[0x01]: ESRESET 0x00
+ GRC[0x02]: COLORCMP 0x00 GRC[0x02]: COLORCMP 0x00
+ GRC[0x03]: DATAROT 0x00 GRC[0x03]: DATAROT 0x00*
+ GRC[0x04]: READMAP 0x00 GRC[0x04]: READMAP 0x00
+ GRC[0x05]: MODE 0x11* GRC[0x05]: MODE 0x00
+ GRC[0x06]: MISC 0x05 GRC[0x06]: MISC 0x05
+ GRC[0x07]: COLORDC 0x0F GRC[0x07]: COLORDC 0x0F
+ GRC[0x08]: BITMASK 0xFF GRC[0x08]: BITMASK 0xFF
+ SEQ[0x00]: RESET 0x03 SEQ[0x00]: RESET 0x03
+ SEQ[0x01]: CLOCKING 0x01 SEQ[0x01]: CLOCKING 0x01
+ SEQ[0x02]: MAPMASK 0x0F* SEQ[0x02]: MAPMASK 0x0F*
+ SEQ[0x03]: CHARMAP 0x00 SEQ[0x03]: CHARMAP 0x00
+ SEQ[0x04]: MEMMODE 0x06 SEQ[0x04]: MEMMODE 0x06
+ FEAT: 0x02 FEAT: 0x02
+ MISC: 0x23 MISC: 0xA7
+ STATUS0: 0x10 STATUS0: 0x10
+ LATCHES: 0x00000000 LATCHES: 0x00000000
+ ACCESS: 0x1411 ACCESS: 0x0400
+
+One of the apparent oddities is that, for mode 0x0E, the GRC Mode Register was programmed with 0x11, whereas
+for mode 0x10, it was programmed with 0x00. Why would mode 0x0E want to set the ODDEVEN bit during the scroll,
+when it hadn't been set during any other writes to the screen?
+
+At this point, I dumped the instruction history buffer a bit ("*dh 100*"), and noticed this GRC write:
+
+ C000:1582 8BC5 MOV AX,BP ;history=33
+ C000:1584 B603 MOV DH,03 ;history=32
+ C000:1586 B2CE MOV DL,CE ;history=31
+ C000:1588 E88AF7 CALL 0D15 ;history=30
+ C000:0D15 86C4 XCHG AL,AH ;history=29
+ C000:0D17 EE OUT DX,AL ;history=28
+ C000:0D18 42 INC DX ;history=27
+ C000:0D19 86C4 XCHG AL,AH ;history=26
+ C000:0D1B EE OUT DX,AL ;history=25
+ C000:0D1C 4A DEC DX ;history=24
+
+So I looked farther back and saw where BP was set:
+
+ C000:1522 BA00A0 MOV DX,A000 ;history=97
+ C000:1525 BD1105 MOV BP,0511 ;history=96
+
+Here's the complete function:
+
+ GR_ST_1:
+ MOV DX,A000
+ MOV BP,0511
+ CMP AH,0F
+ JC 1535
+ CALL 14F7
+ JNC 1535
+ MOV BP,0501
+ RET
+
+OK, so any (EGA) graphics mode below 0x0F is going to the trigger the use of Write Mode 1 with the ODDEVEN bit set.
+And sure enough, the scrolling bug also occurs when using 320x200 16-color mode 0x0D.
+
+It's also worth noting that, whenever the ODDEVEN bit of the GRC Mode Register is set, the SEQUENTIAL bit in the Sequencer
+Memory Mode Register is supposed to be clear (and vice versa -- those two bits are supposed to always be oppositely set).
+But here, the IBM EGA BIOS hasn't done that. One wonders if that was a mistake....
+
+Another bit of trivia: while dumping the frame buffer in a VGA text mode in a different emulator, I discovered that
+when I turned off the ODDEVEN bit in the GRC Mode Register, odd bytes would still appear from plane 1; it wasn't until I
+*also* turned off the CHAIN bit in the GRC Miscellaneous Register that the odd bytes would no longer appear. But,
+that could have just been an idiosyncrasy of that particular emulator.
+
+As a result of all these observations, and more importantly, to make EGA scrolling work properly in modes 0x0D and 0x0E,
+I've changed the Video component to use odd/even memory functions *only* when the SEQUENTIAL bit (bit 2) of the
+Sequencer's Memory Mode Register is clear, instead of relying on the ODDEVEN bit (bit 4) of the Graphics Controller's Mode
+Register.
+
+To be continued.... because I've barely scratched the surface of all the side-effects of EGA/VGA odd/even addressing,
+and I hope to do some testing on real hardware in the near future.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*June 5, 2015*
diff --git a/_posts/2015-07-17-windows-95.md b/_posts/2015-07-17-windows-95.md
new file mode 100644
index 000000000..d353494d0
--- /dev/null
+++ b/_posts/2015-07-17-windows-95.md
@@ -0,0 +1,204 @@
+---
+layout: post
+title: Windows 95
+date: 2015-07-17 11:00:00
+category: Windows 95
+permalink: /blog/2015/07/17/
+machines:
+ - type: pcx86
+ id: deskpro386
+ debugger: true
+ state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
+ config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/debugger/machine.xml
+ drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
+ autoMount: ''
+---
+
+This week (July 14, 2015) was the 20th anniversary of Windows 95 RTM ("Release To Manufacturing"). So I decided to
+throw a PCjs party and try running Windows 95 Setup inside a PCx86 machine for the first time.
+
+Sadly, it immediately failed:
+
+ Please wait while Setup initializes.
+ Windows requires a computer with an 80386 processor or higher.
+
+The failing code:
+
+ 0E36:08FD 06 PUSH ES
+ 0E36:08FE 1E PUSH DS
+ 0E36:08FF 9C PUSHF
+ 0E36:0900 33C0 XOR AX,AX
+ 0E36:0902 50 PUSH AX
+ 0E36:0903 9D POPF
+ 0E36:0904 9C PUSHF
+ 0E36:0905 58 POP AX
+ 0E36:0906 A90080 TEST AX,8000
+ 0E36:0909 7517 JNZ 0922
+ 0E36:090B B80070 MOV AX,7000
+ 0E36:090E 50 PUSH AX
+ 0E36:090F 9D POPF
+ 0E36:0910 FB STI
+ 0E36:0911 9C PUSHF
+ 0E36:0912 58 POP AX
+ 0E36:0913 A90070 TEST AX,7000
+ 0E36:0916 7405 JZ 091D
+ 0E36:0918 B88603 MOV AX,0386
+ 0E36:091B EB08 JMP 0925
+ 0E36:091D B88602 MOV AX,0286
+ 0E36:0920 EB03 JMP 0925
+ 0E36:0922 B88600 MOV AX,0086
+ 0E36:0925 9D POPF
+ 0E36:0926 1F POP DS
+ 0E36:0927 07 POP ES
+ 0E36:0928 C3 RET
+
+was easily fixed with a change to [x86cpu.js](/modules/pcx86/lib/x86cpu.js), allowing the IOPL bits to be
+modified in real-mode on an 80386. When I had previously tweaked setPS() to accomodate 80286/80386 discrimination
+logic in OS/2 1.0, there was no 80386 support in PCx86 at that time, so it was sufficient to *never* allow the
+IOPL bits to be altered in real-mode.
+
+The next problem was triggered by Setup's CAB ("Diamond") decompression code, which uses all 32 bits
+of the 80386's 32-bit registers. That in itself was not a problem, but by leaving stray bits in the upper halves of
+registers like EDX, it exposed a bug in the PCx86 I/O instruction handlers, which neglected to mask EDX with 0xFFFF before
+performing port lookups, causing mysterious I/O failures that usually manifested themselves as hard disk I/O errors.
+
+Then PCx86 crashed in the middle of the decompression of the first CAB file (MINI.CAB). It turned out the stack
+had been improperly adjusted because a "RETF n" instruction mistakenly believed that a stack switch had occurred.
+This was, in fact, a left-over condition from a protected-mode stack-switch. That was easily cleared.
+
+Next, I discovered that I had never finished updating a handful of instructions to full 32-bit operation;
+namely, INC, DEC, NEG, NOT, TEST, MOVSB and MOVSW. Those are done now.
+
+Then I ran into a couple of Windows 95 oddities. First, some kernel initialization code deliberately
+executed an invalid opcode (0x0F,0xFF) and expected its DPMI exception (0x06) handler to field the exception; that was
+fixed by flagging the opcode as genuinely invalid.
+
+> SIDEBAR
+
+> By default, PCx86 marks opcodes as invalid *only* if they have been confirmed invalid. PCx86 considers the vast
+majority of unused/undocumented opcodes to be merely "undefined" until I've seen them in the wild. An instruction will
+be marked invalid if I either discover that real hardware treats it as an Invalid Opcode (by throwing the exception)
+*or* that real software expects it to. For opcode 0x0F,0xFF, it was the latter.
+
+> Don't be confused that Intel also refers to the Invalid Opcode exception (0x06) as the #UD ("Undefined")
+opcode exception. Every opcode that triggers that exception is, by definition, defined: it's defined as invalid.
+I consider it a misnomer to refer to any invalid instruction as "undefined".
+
+The other recent Windows 95 oddity I ran into was an instruction with multiple address-override (0x67) prefixes; the
+first prefix changed the instruction's addressing mode from 16-bit to 32-bit, and the second prefix changed it back to
+16-bit. PCx86 should have simply ignored the second prefix.
+
+With all of the above changes in place, PCx86 v1.18.4 is able to run Windows 95 Setup a bit farther, but still far from
+completion. If you want to give it a spin yourself, start the machine below (click the "Run" button) and once it has
+finished booting, run SETUP from drive B, where the first Windows 95 diskette is already loaded.
+
+If, when it crashes (and it will), you're interested in examining the instructions that were executed prior to the
+crash, you can dump the instruction history buffer using the Debugger's "dh" command.
+
+NOTE: The diskette images contain a pre-release version of Windows 95, as I don't currently have the RTM version on
+diskette.
+
+---
+
+August 13, 2015 Update
+---
+
+PCx86 v1.18.8 has made a little more progress running Windows 95 Setup, but CAB decompression still fails almost
+immediately. To monitor DOS calls until the first 36-byte read of PRECOPY1.CAB, try setting the following
+breakpoint and then starting the machine, using the PCx86 Debugger *input* field next to the **Enter** button:
+
+ m dos off
+ bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"
+ g
+
+Alternatively, you can hard-code those commands into the Debugger component of the machine.xml file; eg:
+
+```xml
+
+```
+
+Once the 36-byte read is hit, you'll probably want to stop on the next instruction that examine those bytes,
+by using a memory read breakpoint:
+
+ br ds:dx
+
+and you also might want to change the first breakpoint to stop on *any* file read, and add a second breakpoint
+to dump the initial contents of those reads:
+
+ bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f"
+ bp FDC8:422A "if fn==3f;di ds:dx;db ds:dx;h;else"
+
+In the current machine, `1ED4:16B4` is the DOS INT 0x21 entry point and `FDC8:422A` is the corresponding IRET.
+The first breakpoint sets an internal variable, `fn`, to the value of the **AH** register on entry, so that the
+second breakpoint can check the value on exit.
+
+Regarding the other Debugger commands shown above, the `dos` command describes the current DOS operation
+(alternatively, you could use the `m int on; m dos on` commands to turn on DOS interrupt messages). The `di`
+command dumps PCx86 BACKTRACK(tm) information, to help you visually confirm which INT 0x21 calls are reading
+PRECOPY1.CAB. Finally, the `if` command evaluates the given expression, and if the result is non-zero ("true"),
+all subsequent commands are executed, up to to any `else` command; otherwise, only commands after the `else`
+command will be executed, and if there is no `else` command, execution will stop.
+
+Debugger expressions may contain the usual variety of arithmetic, bitwise and logical binary operators, and they
+are evaluated using traditional operator precedence (ie, the same as C or JavaScript); any other operators, such as
+parentheses, assignment operators, and unary or ternary operators, are not supported in expressions.
+
+This test machine below has been updated to load WDEB386.EXE prior to starting B:SETUP.EXE, if you prefer using
+WDEB386. Make sure the machine is running (ie, click the **Run** button, or use the PCx86 Debugger "g" command),
+and then click on the Debugger *output* control to give it focus and press CTRL-C to trigger WDEB386.
+
+The Debugger *input* field is used exclusively for PCx86 Debugger commands, whereas the *output* textarea combines
+all Debugger output *and* WDEB386 COM2 serial port I/O. You can even use the PCx86 Debugger to debug
+the WDEB386 debugger; just make sure the appropriate text control has focus before typing a command.
+
+To help reduce confusion, the PCx86 Debugger displays a double-character command prefix, to differentiate its commands
+from WDEB386's single-character command prompt -- but it's still easy to get confused.
+
+A quick recap of those command prefixes (which you won't see until AFTER you've typed a PCx86 command):
+
+ * `>>` indicates real-mode
+ * `##` indicates protected-mode
+ * `--` indicates V86-mode
+
+80386 Debug register (DR0-DR7) support was recently added, so even WDEB386 read/write breakpoints should work now.
+
+---
+
+August 21, 2015 Update
+---
+
+PCx86 v1.19.1 has finally solved a number of nagging bugs. The CAB decompression code itself was running
+fine; it would crash after loading a 32-bit value into EAX *and* a timer interrupt occurred. A path
+through the interrupt handler was trashing the upper bits of EAX. The culprit: any of the MOV instructions
+that move an immediate value into one of the "high" 8-bit registers (AH, BH, CH, or DH). Those instructions
+were failing to preserve the upper 16 bits of the entire register. Further proof that the most exasperating
+bugs sometimes have the most mundane causes.
+
+Also recently fixed: the annoying VGA video glitch that would occur whenever the mouse was moved, improper
+updates to the ACCESSED bit in a selector's descriptor table entry, improper error codes when a selector load
+generated a fault, and the RETF instruction's failure to properly restart when the return address referred
+to a not-present segment.
+
+The next problem appears to be timer-related. When Windows 95 Setup begins its hardware analysis, it
+gets "stuck" in code that's reading and writing timer ports (0x40 and 0x43). Turning on timer port messages
+with the `"m port on;m timer on"` Debugger commands reveals that the problem maybe an unsupported timer
+command:
+
+ chipset.outPort(0x0043,PIT1_CTRL,0xD2) @1847:DD02
+ PIT1_CTRL: Read-Back command not supported (yet)
+
+---
+
+September 12, 2015 Update
+---
+
+Lots of bugs have been squashed in the past few weeks -- not enough to finish setting up Windows 95, but it's getting
+closer. More details on recent releases can be found [here](https://github.com/jeffpar/pcjs/releases).
+
+The machine below has been reconfigured with a hard disk image containing all the Windows 95 Setup files. The machine
+is at the point where Windows 95 SETUP has just rebooted.
+
+{% include machine.html id="deskpro386" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*July 17, 2015 (updated September 12, 2015)*
diff --git a/_posts/2015-09-21-windows-95-in-your-web-browser.md b/_posts/2015-09-21-windows-95-in-your-web-browser.md
new file mode 100644
index 000000000..77eccbd53
--- /dev/null
+++ b/_posts/2015-09-21-windows-95-in-your-web-browser.md
@@ -0,0 +1,39 @@
+---
+layout: post
+title: Windows 95 In Your Web Browser
+date: 2015-09-21 11:00:00
+category: Windows 95
+permalink: /blog/2015/09/21/
+machines:
+ - type: pcx86
+ id: deskpro386
+ state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
+ config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/machine.xml
+ drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
+ autoMount: ''
+---
+
+Today, the last serious bug preventing a successful boot of Windows 95 was fixed. I won't bore you with
+the details.
+
+OK, I will: three arithmetic instructions (specifically, **AND**, **OR** and **XOR**) include a variation that
+converts an immediate signed byte into a signed word. Those variations were failing to truncate the result when
+a 16-bit operand size was in effect, and if the destination was a register, the upper 16 bits of that register
+could become corrupted.
+
+The [Windows 95 Test Machine](/disks/pcx86/windows/win95/4.00.950/) hard disk has been updated
+with a complete set of Windows 95 files from a "Compact" installation, and first boot has finished, so instead
+of the initial "Getting ready to run Windows 95 for the first time..." splash screen, you'll see the normal
+Windows 95 startup screen.
+
+The machine is still a bit finicky. It easily gets confused about the state of its shift keys if you switch away
+from the browser and then back again. And Explorer windows don't open in the correct view; for example, both
+**My Computer** and **Recycle Bin** open the same (incorrect) view. In short, there are still some serious bugs
+to be resolved, but booting has been achieved.
+
+The adventure continues.
+
+{% include machine.html id="deskpro386" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*September 21, 2015*
diff --git a/_posts/2015-10-27-windows-95-and-early-80386-cpus.md b/_posts/2015-10-27-windows-95-and-early-80386-cpus.md
new file mode 100644
index 000000000..8832f13c0
--- /dev/null
+++ b/_posts/2015-10-27-windows-95-and-early-80386-cpus.md
@@ -0,0 +1,218 @@
+---
+layout: post
+title: Windows 95 and Early 80386 CPUs
+date: 2015-10-27 11:00:00
+categories: ['Windows 95', '80386']
+permalink: /blog/2015/10/27/
+---
+
+Every time Windows 95 starts up, its real-mode loader performs the following CPU identification test.
+
+ &0654:121E 9C PUSHF
+ &0654:121F 33C0 XOR AX,AX ; try to clear bit 15 of flags
+ &0654:1221 50 PUSH AX
+ &0654:1222 9D POPF
+ &0654:1223 9C PUSHF
+ &0654:1224 58 POP AX
+ &0654:1225 A90080 TEST AX,8000 ; bit 15 of flags set anyway?
+ &0654:1228 7543 JNZ 126D ; yes (must be an 8086/8086)
+ &0654:122A B80070 MOV AX,7000 ; try to set bits 12-14 of flags
+ &0654:122D 50 PUSH AX
+ &0654:122E 9D POPF
+ &0654:122F FB STI
+ &0654:1230 9C PUSHF
+ &0654:1231 58 POP AX
+ &0654:1232 9D POPF
+ &0654:1233 A90070 TEST AX,7000 ; any of bits 12-14 of flags set?
+ &0654:1236 7436 JZ 126E ; no (must be an 80286)
+ &0654:1238 51 PUSH CX
+ &0654:1239 33C9 XOR CX,CX
+ &0654:123B 8BC4 MOV AX,SP
+ &0654:123D 83E003 AND AX,0003
+ &0654:1240 7404 JZ 1246
+ &0654:1242 8BC8 MOV CX,AX
+ &0654:1244 2BE0 SUB SP,AX
+ &0654:1246 669C PUSHFD
+ &0654:1248 666800000400 PUSH 00040000 ; try to set the AC bit of flags
+ &0654:124E 669D POPFD
+ &0654:1250 669C PUSHFD
+ &0654:1252 6658 POP EAX
+ &0654:1254 669D POPFD
+ &0654:1256 66A900000400 TEST EAX,00040000 ; AC bit set?
+ &0654:125C 7508 JNZ 1266 ; yes (must be an 80486, or newer)
+ &0654:125E 40 INC AX
+ &0654:125F 03E1 ADD SP,CX
+ &0654:1261 59 POP CX
+ &0654:1262 0BC0 OR AX,AX
+ &0654:1264 F9 STC ; return ZF clear and CF set to indicate 80386
+ &0654:1265 C3 RET
+ &0654:1266 03E1 ADD SP,CX
+ &0654:1268 59 POP CX
+ &0654:1269 660BC0 OR EAX,EAX ; return ZF clear and CF clear to indicate 80486
+ &0654:126C C3 RET
+ &0654:126D 9D POPF
+ &0654:126E 33C0 XOR AX,AX ; return ZF set to indicate 80286 or older
+ &0654:1270 C3 RET
+
+If the above function returns ZF set, then the processor is an 80286 or older, so Windows 95 displays the
+following message and exits:
+
+ You need an 80386 processor to run Windows.
+
+If the above function returns CF set, then the processor is an 80386, and if CF is clear, the processor is an
+80486 or newer.
+
+When CF is set, Windows 95 proceeds to the following 80386 stepping check, which attempts to execute an
+XBTS (Extract Bit String) instruction -- an instruction that existed only on B0 and earlier 80386 steppings.
+
+ &0654:1299 53 PUSH BX
+ &0654:129A 51 PUSH CX
+ &0654:129B 52 PUSH DX
+ &0654:129C B80635 MOV AX,3506 ; save the current "invalid opcode" handler (IVT entry #6)
+ &0654:129F CD21 INT 21
+ &0654:12A1 8CC0 MOV AX,ES
+ &0654:12A3 66C1E010 SHL EAX,10
+ &0654:12A7 8BC3 MOV AX,BX
+ &0654:12A9 6650 PUSH EAX
+ &0654:12AB 1E PUSH DS
+ &0654:12AC BAE012 MOV DX,12E0
+ &0654:12AF 8CCB MOV BX,CS
+ &0654:12B1 8EDB MOV DS,BX
+ &0654:12B3 B80625 MOV AX,2506 ; temporarily install a new "invalid opcode" exception handler
+ &0654:12B6 CD21 INT 21
+ &0654:12B8 1F POP DS
+ &0654:12B9 33C0 XOR AX,AX
+ &0654:12BB 8BD0 MOV DX,AX
+ &0654:12BD B900FF MOV CX,FF00
+ &0654:12C0 0FA6CA XBTS CX,DX,AX,CL ; attempt to execute an XBTS instruction
+ &0654:12C3 6658 POP EAX
+ &0654:12C5 8BD0 MOV DX,AX
+ &0654:12C7 66C1E810 SHR EAX,10
+ &0654:12CB 1E PUSH DS
+ &0654:12CC 8ED8 MOV DS,AX
+ &0654:12CE B80625 MOV AX,2506 ; restore the original "invalid opcode" exception handler
+ &0654:12D1 CD21 INT 21
+ &0654:12D3 1F POP DS
+ &0654:12D4 B8B000 MOV AX,00B0
+ &0654:12D7 0BC9 OR CX,CX ; did XBTS work (ie, did CX change)?
+ &0654:12D9 7401 JZ 12DC ; yes, so we must have a B0 stepping or earlier
+ &0654:12DB 40 INC AX ; no, so bump the stepping to B1
+ &0654:12DC 5A POP DX
+ &0654:12DD 59 POP CX
+ &0654:12DE 5B POP BX
+ &0654:12DF C3 RET ; returns AX == 0xB0 if B0 stepping or earlier, 0xB1 if B1 stepping or later
+
+ &0654:12E0 55 PUSH BP ; temporary "invalid opcode" handler
+ &0654:12E1 8BEC MOV BP,SP
+ &0654:12E3 83460203 ADD [BP+02],0003 ; advance IP past the 3-byte XBTS instruction
+ &0654:12E7 5D POP BP
+ &0654:12E8 CF IRET
+
+If the above function returns 0xB0, then the 80386 is a B0 or earlier stepping, so Windows 95 displays the
+following message and aborts:
+
+ Windows may not run correctly with the 80386 processor that is installed in this computer.
+ Upgrade your 80386 processor.
+
+Otherwise, the 80386 is a B1 or later stepping, so Windows 95 next performs a multiplication test (a simplified
+version of the multiplication tests discussed in "[Early 80386 CPUs](/blog/2015/02/23/)"):
+
+ &0654:12E9 33C9 XOR CX,CX
+ &0654:12EB 66BB81000000 MOV EBX,00000081
+ &0654:12F1 66B800A01704 MOV EAX,0417A000
+ &0654:12F7 66F7E3 MUL EBX
+ &0654:12FA 6683FA02 CMP EDX,00000002
+ &0654:12FE 750B JNZ 130B
+ &0654:1300 663D00A0E70F CMP EAX,0FE7A000
+ &0654:1306 7503 JNZ 130B
+ &0654:1308 E2E1 LOOP 12EB
+ &0654:130A C3 RET
+
+If any of the 65,536 identical multiplications return an incorrect result, Windows 95 displays the following
+message:
+
+ WARNING: The 80386 processor in this computer may not reliably execute 32-bit
+ multiplication. Windows may occasionally fail on this computer.
+
+ You may want to replace your 80386 processor.
+ Press any key to continue...Press a key to continue
+
+A multiplication failure implies that the 80386 stepping is B1, because later steppings resolved the problem.
+
+You may have heard that Windows 95 [pulled support for the 80386 B1 stepping](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx),
+and that's true, but only insofar as Windows 95 SETUP is concerned. The following code is executed by WINSETUP.BIN,
+a 16-bit Windows component that manages the Windows 95 installation process:
+
+ #05C7:69E8 1E PUSH DS
+ #05C7:69E9 07 POP ES
+ #05C7:69EA 6657 PUSH EDI
+ #05C7:69EC FD STD
+ #05C7:69ED 66BF00000000 MOV EDI,00000000
+ #05C7:69F3 678A07 MOV AL,[EDI]
+ #05C7:69F6 67AA STOSB
+ #05C7:69F8 33C0 XOR AX,AX
+ #05C7:69FA 6681FFFFFF0000 CMP EDI,0000FFFF
+ #05C7:6A01 7501 JNZ 6A04
+ #05C7:6A03 40 INC AX
+ #05C7:6A04 FC CLD
+ #05C7:6A05 665F POP EDI
+ #05C7:6A07 C3 RET
+
+Ths above code checks for B1 stepping [Errata #7](/pubs/pc/reference/intel/80386/#b1-errata): "Wrong Register Size for
+String Instructions in Mixed 16/32-bit Addressing Systems." It returns AX == 0 if the STOSB instruction updated EDI
+correctly (0xFFFFFFFF) or AX == 1 if EDI is incorrect (0x0000FFFF).
+
+If Errata #7 is detected, then Windows 95 SETUP displays the following message and aborts:
+
+ Setup Error B1: Setup has detected an 80386 processor that is not compatible with this version of Windows.
+ Before you can run this version of Windows, you need to upgrade your processor.
+ Contact your computer manufacturer for more information.
+
+However, if you can get through SETUP, Windows 95 will still run on a B1 stepping. For example, if you installed
+Windows 95 using a newer 80386, and then later "downgraded" the CPU to a B1, Windows 95 would still run. If your B1
+suffered from the multiplication flaw, you would see the 32-bit multiplication warning on start-up, but you could
+still continue to run, and if there was no multiplication problem, you would not see any message at all.
+
+---
+
+PCjs v1.20.0 now supports a "stepping" attribute on the <cpu> element, which you can use to simulate specific
+stepping behavior. For example, a *machine.xml* file with the following CPU definition:
+
+```xml
+
+```
+
+will cause Windows 95 to abort exactly as described as above. Similarly, selecting a 80386 B1 stepping:
+
+```xml
+
+```
+
+will cause Windows 95 to display the 32-bit multiplication warning shown above (PCjs deliberately fails the exact
+multiplication test that Windows 95 performs).
+
+If you want to simulate a B1 stepping that does *not* have the 32-bit multiplication flaw, set the stepping to B2:
+
+```xml
+
+```
+
+B2 was not an actual 80386 stepping; it is a *pseudo-stepping* that provides a simple way of specifying a B1 80386 that
+passes all 32-bit multiplication tests.
+
+As previously discussed, Windows 95 SETUP will refuse to install on any "A" or "B" stepping, but if it's already been
+installed, it *will* start up on a B1 stepping.
+
+PCjs stepping support is extremely limited at this point. Here's a summary:
+
+1. 80386 steppings A0-B0 provide *limited* support for the short-lived XBTS and IBTS instructions
+2. 80386 steppings A0-B1 enable [Errata #7](/pubs/pc/reference/intel/80386/#b1-errata) for STOSB (as tested by Windows 95; see above)
+3. 80386 stepping B1 enables 32-bit multiplication errors (as tested by Windows 95; see above)
+4. 80386 stepping B2 includes all supported B1 errata, but without 32-bit multiplication errors
+
+In addition, on 80386 reset, we set the CPU revision number in DX to the appropriate value for the specified stepping.
+
+Support for additional 80286 and 80386 errata may be added over time, as interesting scenarios or test cases are discovered.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*October 27, 2015*
diff --git a/_posts/2015-12-10-rebuilding-the-pcjs-website.md b/_posts/2015-12-10-rebuilding-the-pcjs-website.md
new file mode 100644
index 000000000..91781d068
--- /dev/null
+++ b/_posts/2015-12-10-rebuilding-the-pcjs-website.md
@@ -0,0 +1,124 @@
+---
+layout: post
+title: Rebuilding the PCjs Website
+date: 2015-12-10 11:03:00
+category: Website
+permalink: /blog/2015/12/10/
+---
+
+It's been nice using Node.js to power the PCjs website, using Amazon's Elastic Beanstalk service, but that combination
+has also been a source of some frustrations.
+
++ When someone posts an article or a tweet linking to a PCjs page, the website bogs down, and while Amazon's Elastic
+Beanstalk service makes it easy to automatically scale up, each new instance automatically multiplies my expenses as well,
+which hover around $34/month for a single instance. With assorted S3 and transfer charges, my average monthly bill is
+over $55/month. That's a bit much for a site that generates zero revenue.
+
++ Once or twice a year, when I'm attempting to either update the server or upgrade my Node configuration, the update
+or upgrade will fail, and Amazon's web console provides virtually no details about why it failed. I get an error message
+like "**ERROR: Failed to deploy application**" and that is it. Literally.
+
+Granted, there are some "simple" things I could do to improve performance, like adding an **nginx** proxy server to
+the configuration, but that feels like a band-aid solution, and as a software developer, the time spent fiddling with
+web server issues is time I'd much rather spend writing code.
+
+Since PCjs is designed to do all its work in the user's web browser, and since the website can be completely built out
+as a set of static web pages, I've decided to stop using Node to power www.pcjs.org. I'm in the process of migrating
+the website to [GitHub Pages](https://pages.github.com/), and using [Jekyll](https://help.github.com/articles/using-jekyll-with-pages/)
+to convert all my existing Markdown files to HTML.
+
+This new approach is *very* similar to what the PCjs custom Node modules did: every time someone visited a folder
+on the website that did not yet contain an "index.html", the PCjs Node server would create one, either by converting the
+README.md file in that folder to HTML or generating a default HTML document. The PCjs Markdown-to-HTML converter also
+contained some special logic that made it easy to embed PCjs machines on a page.
+
+Thanks to GitHub Pages, all of that now happens ahead of time: whenever I update the PCjs "gh-pages" branch on GitHub,
+Jekyll automatically runs through the entire site and rebuilds a complete set of web pages.
+
+The Node web server would automatically embed PCjs machines on web pages by looking for special Markdown links, such as;
+
+ [Embedded IBM PC](machine.xml "PCjs:ibm5150")
+
+After migrating to GitHub Pages and Jekyll, that markup must now be written as:
+
+{% raw %}
+ {% include machine.html id="ibm5150" %}
+{% endraw %}
+
+and the following "Front Matter" (YAML) must appear at the top of the Markdown file:
+
+ ---
+ ...
+ machines:
+ - type: pcx86
+ id: ibm5150
+ ---
+
+Basically, the YAML at the top of the file lists all the machines that the page intends to use, and then
+{% raw %}`{% include ... %}`{% endraw %} is inserted in the text at the point where a machine should be embedded.
+
+The `machine.html` include accepts only one parameter: the `id` of a machine listed at the top of the file.
+
+The `machines` element at the top of the file must specify a `type` and `id` at a minimum. `type` should be one of
+the following values, depending on whether you want an IBM PC or Challenger 1P:
+
+- pc
+- c1p
+
+and `id` can be any identifier you want to use to embed the machine. If you want to include the machine's built-in
+debugger, set `debugger` to *true*; the older method of specifying *pc-dbg* or *c1p-dbg* instead of *pc* or *c1p* as the
+`type` still works, too.
+
+You may also use `config` to specify a machine XML configuration file if not using the default *machine.xml*;
+`template` to specify an alternate XSL template file if not using the default *components.xsl*; `state` to specify
+a JSON-encoded machine state file if the machine requires a predefined state; and `uncompiled` may be set to *true*
+to force a machine to use uncompiled sources, overriding the value of `site.pcjs.compiled` in **_config.yml**.
+
+For example, the PCjs home page contains two machines, so this appears at the top of the
+[Markdown file](https://raw.githubusercontent.com/jeffpar/pcjs/master/index.md):
+
+ machines:
+ - type: pcx86
+ id: ibm5150
+ config: /devices/pcx86/machine/5150/mda/64kb/machine.xml
+ - type: c1p
+ id: demoC1P
+ config: /devices/c1p/machine/8kb/large/machine.xml
+
+If necessary, you can also override some of the settings in a machine XML file. Here's an example of overriding the
+FDC `autoMount` setting, making it easy to reuse the same machine XML file with different boot disks:
+
+ machines:
+ - type: pcx86
+ id: deskpro386
+ debugger: true
+ autoMount:
+ A:
+ path: /disks/pcx86/os2/misc/football/FOOTBALL-76817.json
+ config: /devices/pcx86/machine/compaq/deskpro386/ega/4096kb/debugger/machine.xml
+
+Other settings that can currently be overridden include:
+
+ + `autoPower`
+ + `drives`
+ + `messages`
+ + `state`
+
+Additional overrides will be added as needed. See the [Windows 95 Demo](/disks/pcx86/windows/win95/4.00.950/)
+machine and its associated [Markdown file](https://raw.githubusercontent.com/jeffpar/pcjs/master/disks/pcx86/windows/win95/4.00.950/README.md)
+for more override examples, including how to set `autoMount` to *not* mount any diskettes.
+
+I will continue to include a Node web server with the PCjs Project, but it's now intended for development
+purposes only (not production servers). I've updated the PCjs MarkOut component to parse any "Front Matter"
+at the top of the PCjs Markdown files, and to convert all new Jekyll-style embedded machines and screenshots
+to the older Markdown-compatible formats, so pages generated by the Node web server should still function
+as before. But there are no guarantees.
+
+WARNING: If you decide to run/alternate between Jekyll's web server (WEBrick) and the built-in Node web
+server (server.js), you should run "grunt clean" before starting either one, to remove any old **index.html**
+files. Node may inadvertently reuse old "index.html" files, and Jekyll may inadvertently propagate them
+to its "_site" folder. It's easy to tell when this happens, because you'll see the wrong color scheme: Node
+web server pages were designed to use *dark* colors, whereas Jekyll web server pages currently use *light* colors.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*December 10, 2015*
diff --git a/_posts/2015-12-27-revisiting-os2.md b/_posts/2015-12-27-revisiting-os2.md
new file mode 100644
index 000000000..728a765e9
--- /dev/null
+++ b/_posts/2015-12-27-revisiting-os2.md
@@ -0,0 +1,48 @@
+---
+layout: post
+title: Revisiting OS/2
+date: 2015-12-27 11:00:00
+category: OS/2
+permalink: /blog/2015/12/27/
+machines:
+ - type: pcx86
+ id: ibm5170
+ debugger: true
+ config: /devices/pcx86/machine/5170/ega/2048kb/rev3/debugger/machine.xml
+ drives: '[{name:"20Mb Hard Disk",type:2,path:"/disks/pcx86/fixed/20mb/IBMOS210-EGA.json"}]'
+ automount: ''
+---
+
+Just for fun (because I have a warped sense of fun), I decided to revisit some of the old OS/2 software I wrote
+almost 30 years ago. But first, I needed an OS/2 development environment.
+
+So I started with a clean install of [IBM OS/2 1.0](/disks/pcx86/os2/ibm/1.0/) in the 8Mhz IBM PC AT machine
+below, by booting from the "IBM OS/2 1.0 (1.44M Install)" diskette in drive A and reformatting the machine's 20Mb
+drive C.
+
+Next, I installed the [MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/). This SDK was released
+in December 1987 along with [Microsoft OS/2 1.0](/disks/pcx86/os2/microsoft/1.0/). I don't have any of the
+printed documentation that came with the SDK, such as the *Installation Guide*, but I do have the
+[Microsoft® Operating System/2 Programmer’s Toolkit](/pubs/pc/software/os2/microsoft/ptk10/) documentation
+from March 1988, thanks to the [OS/2 Museum](http://www.os2museum.com/wp/os2-history/os2-library/os2-1-x-programming/).
+
+Aside from **Microsoft Macro Assembler 5.00A** (MASM) and **Microsoft C Compiler 5.10 (Beta)** (CL), the SDK
+included some other useful tools, such as the **SDK Editor** (SDKED), which was essentially an OS/2 port of
+Mark Zbikowski's full-screen editor (Z) that was used internally at Microsoft for many years. It was renamed
+to the **Microsoft Editor** (M or MEP) with the release of **Microsoft C Compiler 5.10**, and it was later integrated
+into **Programmer's Workbench** (PWB), the text-mode Integrated Development Environment (IDE) that came with
+**Microsoft C Compiler 6.0**.
+
+With the introduction of graphical IDEs, such as Visual BASIC in 1991, Visual C++ in 1993, and Visual Studio in 1995,
+this stand-alone, text-mode editor became obsolete, but in the 1980s, it was a valuable tool. You can learn more
+about [SDKED](/disks/pcx86/tools/microsoft/os2/sdk/1.02/#using-sdked) on the
+[MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/) page.
+
+Our [IBM OS/2 1.0](/disks/pcx86/os2/ibm/1.0/) demo machine (shown below) has the
+[MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/) pre-installed, so check out our copy of the
+[Microsoft® Operating System/2 Programmer’s Toolkit](/pubs/pc/software/os2/microsoft/ptk10/) and then write some code!
+
+{% include machine.html id="ibm5170" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*December 27, 2015*
diff --git a/_posts/2016-01-23-early-os2-artifacts.md b/_posts/2016-01-23-early-os2-artifacts.md
new file mode 100644
index 000000000..ae6b3d811
--- /dev/null
+++ b/_posts/2016-01-23-early-os2-artifacts.md
@@ -0,0 +1,39 @@
+---
+layout: post
+title: Early OS/2 Artifacts
+date: 2016-01-23 14:00:00
+category: OS/2
+permalink: /blog/2016/01/23/
+---
+
+Before OS/2 was named **OS/2** by IBM on April 2, 1987, the operating system was known by many different names at
+Microsoft as it evolved, including **DOS5**, **MT-DOS**, **CP-DOS**, and **ADOS**.
+
+In late 1986, Microsoft began working on a couple different branches. One was called **SIZZLE**, where a variety of
+performance improvements were tested before being merged back into the main branch.
+
+Another branch was **FOOTBALL** (aka **PIGSKIN**), an early 80386-based prototype intended to test the viability
+of the running multiple DOS applications in V86-mode. Sometimes this 80386 version was also called **386DOS**,
+to distinguish it from **286DOS**. More details are in this
+[FOOTBALL Design Document](/disks/pcx86/os2/misc/football/87058/#football-design-document).
+
+To shed some light on those efforts, I recently added a few [OS/2 Prototype Disks](/disks/pcx86/os2/misc/): a small
+collection of early (mostly pre-1.0) OS/2 boot disks that provide a glimpse of what some of those early OS/2 builds
+looked like.
+
+Getting these early versions of OS/2 to run in **PCjs** has been a bit of a challenge. There have been some successes
+but also some lingering issues. Debugging continues.
+
+Part of the problem is that these pre-1.0 builds still contain a few bugs. Also, the original
+[OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/) from February 1987 was developed and
+tested exclusively on Compaq DeskPro 386 machines from late 1986, so it has some uncommon 80386 dependencies:
+
+* The [80386 LOADALL](/pubs/pc/reference/intel/80386/loadall/) instruction
+* 32-bit segment register writes must modify only 16 bits of memory
+
+**FOOTBALL** also had some specific video hardware requirements: CGA or EGA. Note that the VGA, which is what most
+emulators use by default these days, did not exist in 1986. The VGA was introduced in April 1987, when IBM
+unveiled their new PS/2 hardware line -- and announced OS/2.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*January 23, 2016*
diff --git a/_posts/2016-02-08-super-bowl-winner-pcjs.md b/_posts/2016-02-08-super-bowl-winner-pcjs.md
new file mode 100644
index 000000000..67d032fa8
--- /dev/null
+++ b/_posts/2016-02-08-super-bowl-winner-pcjs.md
@@ -0,0 +1,57 @@
+---
+layout: post
+title: "Super Bowl Winner: PCjs"
+date: 2016-02-08 14:00:00
+permalink: /blog/2016/02/08/
+---
+
+The new release of PCjs (v1.20.8) is a fairly minor update, but it's an important one for **FOOTBALL** fans, resolving
+two annoying problems with the [OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/): mysterious hard-error popups
+and blank screens.
+
+A hard-error popup would occur when FOOTBALL tried to initialize a non-existent PRN device. To resolve that, PCjs
+now provides basic parallel port emulation, in the form of a [ParallelPort](/docs/pcx86/parallel/) component that you
+include in a machine XML file with the <parallel> element, in much the same way you include the
+[SerialPort](/docs/pcx86/serial/) component with the <serial> element. This [Compaq DeskPro 386]
+(/devices/pcx86/machine/compaq/deskpro386/ega/4096kb/debugger/) machine used to run FOOTBALL has now been updated to
+include one parallel port.
+
+The other problem was that switching between sessions with the **SysReq** key would often result in a blank screen;
+the new session was active, but you couldn't see it. This was a side-effect of how FOOTBALL reprograms the video
+controller when switching screens *and* the linear page mappings it creates to access video memory, which in turn
+exposed a PCjs memory-management bug. The upshot is that whenever the Video component moves the address of the video
+buffer (which is *physical* memory), it must tell the CPU to flush any linear-to-physical mappings that may still refer
+to the old physical memory.
+
+With these changes, the [OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/) appears to be quite usable now.
+Feel free to give it a few kicks!
+
+---
+
+I've also tidied up a few things in the [Devices](/devices/) folder. ROM images used to be stored under
+`/devices/pcx86/basic/` and `/devices/pcx86/bios/`, but BASIC and BIOS ROM images aren't actually devices; they are the
+*contents* of ROM devices. So, with that in mind, I've made the following rearrangements:
+
+* `/devices/pcx86/bios/5150/*` => `/devices/pcx86/rom/5150/*`
+* `/devices/pcx86/bios/5160/*` => `/devices/pcx86/rom/5160/*`
+* `/devices/pcx86/bios/5170/*` => `/devices/pcx86/rom/5170/*`
+* `/devices/pcx86/bios/compaq/*` => `/devices/pcx86/rom/compaq/*`
+* `/devices/pcx86/basic/ibm-basic-1.00.json` => `/devices/pcx86/rom/5150/basic/BASIC100.json`
+* `/devices/pcx86/basic/ibm-basic-1.10.json` => `/devices/pcx86/rom/5160/basic/BASIC110.json`
+
+This structure mirrors what was done with Machine and Video devices, where the devices are organized
+first by manufacturer (IBM or COMPAQ) and then by type (MDA, CGA, EGA, etc).
+
+Here, the first ROM subdivision is either a manufacturer or a machine model. A model implies a manufacturer;
+for example, models 5150 through 5170 refer to IBM PC models.
+
+---
+
+The project currently includes only two BASIC ROM versions, C1.00 and C1.10, which were initially released with the
+first model 5150 and 5160 machines, respectively. For more details, see [IBM PC ROMs](/devices/pcx86/rom/).
+
+I believe there was also a BASIC ROM version 1.20 released for IBM PCjr, but since PCjs does not yet emulate the PCjr,
+it has not been added to the project.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*February 8, 2016*
diff --git a/_posts/2016-02-17-saving-disks-and-machines.md b/_posts/2016-02-17-saving-disks-and-machines.md
new file mode 100644
index 000000000..850d27c8a
--- /dev/null
+++ b/_posts/2016-02-17-saving-disks-and-machines.md
@@ -0,0 +1,130 @@
+---
+layout: post
+title: "Saving Disks and Machines"
+date: 2016-02-17 14:00:00
+permalink: /blog/2016/02/17/
+---
+
+PCx86 (v1.20.9) now offers new, *much* easier ways to save disks and machines, thanks to the new
+[Save Disk](/blog/2016/02/17/#saving-disks) and [Save Machine](/blog/2016/02/17/#saving-machines) features.
+With one click, PCx86 can now generate a single download containing everything you need to embed any of our
+IBM PC demos on your own web page.
+
+Saving Disks
+---
+
+Floppy disk images can now be saved to your desktop computer by simply clicking the **Save** button next
+to the floppy disk controls. Select the drive first, and then whatever diskette is shown as being "loaded"
+in that drive will be saved in your local machine's Downloads folder when you click **Save**.
+
+If you made any changes to that disk after it was loaded, those changes will be included, so if you want a pristine
+copy of the disk, click the **Load** button first. PCx86 will ask you to confirm that you really want to reload the
+disk and discard any changes.
+
+Note that the disk *should* be downloaded as an **.img** file, which is nothing more than a sector-by-sector binary
+dump of the disk. For example, a 360Kb double-sided double-density (DSDD) disk contains 9 512-byte sectors in each
+of the 40 tracks on each of its 2 sides, so when you **Save** a 360Kb disk, the downloaded file should be exactly
+368,640 bytes large.
+
+The Mac OS X operating system can automatically mount most **.img** files (from disks created by DOS 2.0 or later).
+Older DOS 1.x disks, along with most non-DOS disks, do not have a BIOS Parameter Block (BPB) in the boot sector, so
+most modern operating systems won't recognize the disk format. Other operating systems, like Windows, may require
+third-party software in order to mount an **.img** file, and some third-party software may prefer a different extension,
+such as **.ima** or **.bin**.
+
+It's also recommended that you make your **.img** files *read-only*, so that if you do mount them on your desktop
+computer, neither you nor the operating system will inadvertently modify the contents of the disk. On OS X, this is
+easily done with the **chmod** utility.
+
+For example, if you saved the disk named "PC-DOS 2.00 (Disk 1)", it should have been downloaded as "PCDOS200-DISK1.img"
+in your Downloads folder, so the OS X Terminal command `chmod -w PCDOS200-DISK1.img` will make it read-only, and
+`chmod +w PCDOS200-DISK1.img` will make it writable again.
+
+**NOTE**: Some browsers, notably Safari, do not support named downloads, so any disks you download will end up
+with default names like "Unknown" or "download". PCx86 will still try to let you know what the original filename was,
+so that you can rename it appropriately.
+
+Saving Machines
+---
+
+Saving the entire state of any existing IBM PC machine is also much simpler now, using the new **Save Machine** link.
+You can choose to save a machine in its initial state, or make changes to any of the machine's disks and then save it.
+All your changes should be preserved.
+
+Under the bottom-left corner of any IBM PC on the PCjs [website](/), you should now see a
+[**Save Machine**] link. When you click that link, PCx86 will generate a large chunk of JavaScript containing
+everything that machine needs to run, including:
+
+ * The machine XML configuration file (eg, "machine.xml")
+ * The machine XSL transformation file (eg, "components.xsl")
+ * The machine CSS stylesheet file (eg, "components.css")
+ * The machine state file (eg, "state.json")
+ * The PCx86 machine emulation script (eg, "pcx86.js")
+ * Copies of all the disk images mounted by the machine
+
+Let's say you want to save the IBM PC on the PCx86 [home page](/). When you click **Save Machine**, two things should
+happen:
+
+ * A file will be downloaded (eg, "pcx86.js")
+ * A dialog box will appear with some markup to copy-and-paste
+
+The dialog box should provide the following information:
+
+ Check your Downloads folder for "pcx86.js", copy it to your web server as "pcx86.js",
+ and then add the following to your web page:
+
+
+ ...
+
+
+
+ The machine should appear where the is located.
+
+Copy the downloaded file to your own web server as **pcx86.js**, then create or edit a web page and insert the above text.
+If **pcx86.js** and your web page are in different folders, then you'll also need to update *src* to include the exact
+location of the script.
+
+Some notes:
+
+ * PCx86 may attempt to name the downloaded file **pcx86.json** instead of **pcx86.js**, because a file with a ".js"
+ extension could cause your web browser to block the download.
+
+ * For browsers that don't support named downloads, PCx86 will attempt to open a new window/tab instead. Make sure
+ you copy the *entire* contents of that window into a file named to **pcx86.js** (or **pcx86-dbg.js** if the machine is
+ using the built-in PCx86 debugger).
+
+ * Your browser may also impose size limitations on the download. If nothing happens, the machine data may be too
+ large for your browser; try a different browser (eg, Firefox or Safari) or a different machine.
+
+ * If you have modified any floppy disks mounted by the machine *or* any of the machine's hard disks, those
+ modifications should be saved along with the machine.
+
+ * Any floppy disks mounted during the lifetime of the machine will be added to the machine's state. So, for example,
+ if you want your copy of the machine to include all the Windows 1.01 SDK disks, make sure you have loaded each disk
+ once before clicking **Save Machine**.
+
+ * Any floppy disks *not* mounted during the lifetime of the machine will be *removed* from the machine's list of
+ available disks; we don't want machines running on other websites to be consuming our bandwidth.
+
+ * The machine's current state, including memory and screen contents, are saved as part of the machine state.
+ So, even if the original machine always powers on from scratch, the *copied* machine will always resume at the point
+ it was saved. This behavior, however, can be disabled by passing a *parms* object as the 4th parameter to the
+ *embedPC()* call, overriding the 'state' property:
+
+ ```xml
+
+ ```
+
+While the [PCx86 Documentation](/docs/pcx86/) explains how to create a *new* machine, by writing your own machine
+XML file and manually copying all the other pieces, the new **Save Machine** feature is the best way to save
+any *existing* IBM PC and embed it on any other website.
+
+**WARNING**: While this feature is still "hot of the press," there will probably be some kinks to work out. Every
+browser seems to have its own idiosyncrasies in terms of what can be downloaded and/or how large it can be. It's
+also quite possible that certain machine features or modifications may not be properly preserved.
+
+If you're having trouble with a particular browser or a particular machine, be sure to
+[let me know](mailto:Jeff@pcjs.org), and then try another browser and/or machine.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*February 17, 2016*
diff --git a/_posts/2016-02-24-remembering-compaq.md b/_posts/2016-02-24-remembering-compaq.md
new file mode 100644
index 000000000..7b02cc1ca
--- /dev/null
+++ b/_posts/2016-02-24-remembering-compaq.md
@@ -0,0 +1,95 @@
+---
+layout: post
+title: Remembering COMPAQ
+date: 2016-02-24 14:00:00
+permalink: /blog/2016/02/24/
+---
+
+IBM is obviously the company everyone thinks of first when we talk about the IBM PC -- after all, IBM is right
+there in the name. They designed the thing. So IBM, and in particular the folks who worked in Boca Raton at IBM's
+Entry Systems Division in the early 1980's, deserve all the credit for defining what eventually became known as the
+Industry Standard Architecture (ISA) PC platform.
+
+However, the dawn of the PC era also saw the rise and fall of many other personal computer companies.
+One of the more exceptional companies from that era was COMPAQ. It would be a mistake to dismiss COMPAQ as just
+another company that set out to "copy" or "clone" IBM's design, because from the very beginning, COMPAQ's ambitions
+went beyond mere imitation. They were really in the *compatibility* business.
+
+Innovation in the PC industry would not have been as rapid and diverse if IBM had been the only supplier of
+compatible machines and accessories. If IBM had been able to control the market, they probably would have continued
+some of the monopolistic practices they had perfected in the mainframe era. Market control might have been a win for
+IBM, but it almost certainly would *not* have been a win for customers.
+
+In 1982, the founders of COMPAQ took a big gamble, by reportedly investing around a million dollars to create
+a system that mimicked IBM's without infringing on it. I don't know if COMPAQ pioneered the concept of "clean room"
+design, where the engineers designing and coding have no direct contact with the hardware and software they're cloning,
+but they were certainly successful.
+
+But that wasn't COMPAQ's most important contribution. That was just the price they had to "pay to play." Armed with
+compatible hardware and software, COMPAQ proceeded to innovate in ways that IBM did not.
+
+Look at COMPAQ's first product: the COMPAQ Portable. For starters, it was *portable*. And unlike
+IBM machines, it didn't force you to choose between a higher-quality text-only (MDA) display or a lower-quality
+graphics-capable (CGA) display. COMPAQ improved on IBM's mutually-exclusive display choices by creating a monochrome
+display that could display both high-quality text *and* graphics.
+
+IBM released their own portable unit a year later, but it looked suspiciously COMPAQ-like, and it lacked COMPAQ's
+innovative display.
+
+That tradition of innovation at COMPAQ continued for many years. Sometimes they even seemed *more* concerned about
+compatibility than IBM did. For example, faster machines sometimes broke speed-sensitive floppy-based copy protection
+schemes, which COMPAQ tried to avoid by automatically *slowing* the machine down whenever a floppy drive was spinning.
+
+To my knowledge, IBM never bothered with such a feature -- maybe IBM was simply being pragmatic, or perhaps they were
+just a little arrogant, operating on the theory that if the universe revolves around IBM, then the planets will
+simply realign *themselves* (translation: software vendors will release new versions if they want to remain IBM-compatible).
+
+Sadly, COMPAQ was ultimately absorbed by another behemoth -- Hewlett Packard -- and then simply disappeared. Today,
+all we have are the memories.
+
+### Speaking of Memories
+
+In particular, Read-Only Memories: we have precious few of those, too. I finally obtained
+a [ROM Dump](/devices/pcx86/rom/compaq/portable/) from COMPAQ's first machine, the COMPAQ Portable, but I had to buy
+a system board on ebay to get it. Considering all the effort (and money) that COMPAQ invested in writing that code,
+it's a bit depressing that these things haven't been properly preserved and memorialized.
+
+Looking ahead to the day when PCjs will be able to simulate the original COMPAQ Portable, I thought it would be a good
+idea to create my own roughly chronological list of [COMPAQ Machines](/devices/pcx86/machine/compaq/) from the 1980s,
+since they are all machines I would like to see PCjs eventually support:
+
+ - COMPAQ Portable
+ - COMPAQ [Portable] Plus
+ - COMPAQ DeskPro
+ - COMPAQ DeskPro 286
+ - COMPAQ Portable 286
+ - COMPAQ Portable II
+ - [COMPAQ DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/)
+ - COMPAQ Portable III
+ - COMPAQ DeskPro 386/20
+ - COMPAQ Portable 386
+ - COMPAQ DeskPro 386/25
+ - COMPAQ DeskPro 386s
+ - COMPAQ DeskPro 386/20e
+ - COMPAQ SLT Series (SLT/286)
+ - COMPAQ DeskPro 386/33
+ - COMPAQ LTE Series (LTE and LTE/286)
+
+It's hard to stop at this point, because COMPAQ produced other innovative 80386-based systems, like those in the LTE
+and LTE Lite series. But I think I need to stick to my original plan and draw a line at the end of the 1980s.
+
+### Regarding The COMPAQ Name
+
+As best I can tell, COMPAQ preferred to print its company name in all-caps, so that's my practice as well.
+
+However, it seems that sometime between the release of [COMPAQ MS-DOS 3.10](/disks/pcx86/dos/compaq/3.10/) and
+[COMPAQ MS-DOS 3.31](/disks/pcx86/dos/compaq/3.31/), there may have been a shift in policy. Both products still
+called themselves `The COMPAQ Personal Computer MS-DOS`, but in 3.31, the copyright string changed to
+`Compaq Computer Corp.`
+
+Their all-caps practice also extended to product names (eg, `COMPAQ DESKPRO`), at least in their marketing literature.
+Contemporary news stories, however, tended to lower-case the product name (eg, `COMPAQ Deskpro`). I've decided to
+split the difference and use mixed-case where it seems appropriate (eg, `COMPAQ DeskPro`).
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*February 24, 2016*
diff --git a/_posts/2016-03-06-touching-windows.md b/_posts/2016-03-06-touching-windows.md
new file mode 100644
index 000000000..be74895f1
--- /dev/null
+++ b/_posts/2016-03-06-touching-windows.md
@@ -0,0 +1,48 @@
+---
+layout: post
+title: Touching Windows
+date: 2016-03-06 09:00:00
+permalink: /blog/2016/03/06/
+---
+
+Yesterday, I fired up [Windows 3.1](/disks/pcx86/windows/3.10/) and played a complete game of
+[Windows Solitaire](https://en.wikipedia.org/wiki/Microsoft_Solitaire) on my iPad. It was a bit, um, touchy,
+but it worked.
+
+{% comment %}[

](/blog/images/ipad-solitaire.jpg){% endcomment %}
+{% include screenshot.html src="/blog/images/ipad-solitaire-small.jpg" title="iPad running Solitaire on Windows 3.10" link="/blog/images/ipad-solitaire.jpg" %}
+
+The basic touch events are:
+
+ - `touchstart`
+ - `touchmove`
+ - `touchend`
+
+which roughly correspond to `mousedown`, `mousemove`, and `mouseup` events, except that your finger isn't
+exactly like a mouse -- it doesn't have *buttons* for one thing -- so there are several usability problems
+that must be solved.
+
+One of the problems is how to deal with the *default behaviors* of touch events. For example, tapping on a
+PCjs screen is normally how you trigger the iPad's soft keyboard, and dragging your finger across the screen
+normally scrolls the page up and down. However, you don't want *either* of those behaviors when you're
+trying to move the machine's mouse pointer around or click on things.
+
+PCjs attempts to resolve the tapping problem by disabling default behaviors most of the time, except when
+two taps occur more than 1/2 second apart. This allows a quick *double-tap* to be treated as a double-click,
+while a pair of slower taps allows the soft keyboard to activate.
+
+Another problem is how to differentiate between *moving the mouse* (ie, without any buttons pressed) and
+*dragging the mouse* (ie, with the left button pressed). Newer devices offer the option of relying on "3D Touch"
+(aka Force Touch), and using pressure to determine the user's intent, but most devices don't have that feature,
+and I'm not sure I'd want to rely on that anyway.
+
+The PCjs solution: if you *tap and hold* for more than 200ms, and then start moving, your movements become the
+equivalent of a *mouse drag* operation.
+
+This is my first stab at touch-to-mouse conversion, and it's not perfect, but it's already fairly usable,
+as my Solitaire experiment demonstrates. And there are still some open questions, such as how to intuitively
+support operations like *right drag*, since the current *mouse drag* operation is inherently *left drag*;
+one solution would be to rely on multi-touch and use two fingers to trigger right-button operations.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*March 6, 2016*
diff --git a/_posts/2016-03-12-demos-of-windows386-and-windows-3x.md b/_posts/2016-03-12-demos-of-windows386-and-windows-3x.md
new file mode 100644
index 000000000..245c89fca
--- /dev/null
+++ b/_posts/2016-03-12-demos-of-windows386-and-windows-3x.md
@@ -0,0 +1,34 @@
+---
+layout: post
+title: Demos of Windows/386 and Windows 3.x
+date: 2016-03-12 14:00:00
+permalink: /blog/2016/03/12/
+---
+
+I recently added some more demos to the PCjs Project, to showcase its ability to run old 80286-based and
+80386-based software, such as [Windows/386](/disks/pcx86/windows/2.0x/), [Windows 3.0](/disks/pcx86/windows/3.00/),
+[Windows 3.1](/disks/pcx86/windows/3.10/), and [Windows 95](/disks/pcx86/windows/win95/4.00.950/).
+
+{% include screenshot.html src="/disks/pcx86/windows/2.0x/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows/386 2.01" link="/disks/pcx86/windows/2.0x/" %}
+{% include screenshot.html src="/disks/pcx86/windows/3.00/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/EGA, Windows 3.00" link="/disks/pcx86/windows/3.00/" %}
+{% include screenshot.html src="/disks/pcx86/windows/3.10/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/VGA, Windows 3.10" link="/disks/pcx86/windows/3.10/" %}
+{% include screenshot.html src="/disks/pcx86/windows/win95/4.00.950/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows 95" link="/disks/pcx86/windows/win95/4.00.950/" %}
+
+As the [OS/2 Museum](http://www.os2museum.com/wp/windows386-2-01/) points out, [Windows/386 2.01](/disks/pcx86/windows/2.0x/)
+was the first Microsoft product to specifically target the 80386. However, not only was it *not* a 32-bit operating
+system, it didn't even run Windows applications in protected-mode. Windows apps ran in V86-mode, and even then, *only*
+if you started Windows by running `WIN386.EXE`.
+
+There may have been some incidental protection advantages to running Windows in V86-mode instead of real-mode,
+but the primary advantages were the ability to simulate expanded memory (EMS) using the 80386's paging capabilities,
+and the ability to run multiple DOS applications simultaneously.
+
+Alternatively, you could start Windows/386 with `WIN86.COM`, which reverted to the older Windows 1.x memory model,
+where all Windows applications ran in real-mode and only one DOS application could be run at a time.
+
+Strangely, unlike all previous and subsequent versions of Windows, there was no `WIN` command in Windows/386.
+
+At least there was no `LOSE` command.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*March 12, 2016*
diff --git a/_posts/2016-04-30-the-intel-8080-cpu.md b/_posts/2016-04-30-the-intel-8080-cpu.md
new file mode 100644
index 000000000..8f4006a6a
--- /dev/null
+++ b/_posts/2016-04-30-the-intel-8080-cpu.md
@@ -0,0 +1,56 @@
+---
+layout: post
+title: Introducing the Intel 8080 CPU
+date: 2016-04-30 14:00:00
+permalink: /blog/2016/04/30/
+---
+
+Or rather, introducing [PC8080](/modules/pc8080/), a new 8080-based machine emulator recently added to the
+PCjs Project.
+
+Our first [8080 Test Machine](/devices/pc8080/machine/exerciser/) loads a copy of the
+[8080 Exerciser](https://web.archive.org/web/20151006085348/http://www.idb.me.uk/sunhillow/8080.html)
+(specifically, [8080EX1](/devices/pc8080/rom/exerciser/8080EX1.MAC)) and intercepts the exerciser's CP/M console
+calls so that you can see its progress in the Control Panel window. It's a "headless" test machine
+(no keyboard or display), so that's all you get.
+
+The good news: PC8080 passes all the 8080 Exerciser tests. And it doesn't do it by using all sorts of weird
+"flags tables" that most other 8080 emulators seem to fall back on.
+
+Like all the other CPU emulations in the PCjs Project, PC8080 never "calculates" the flags unless/until they are
+actually required, which considerably speeds up arithmetic operations.
+
+Of particular note are the 8080's subtract, compare, and decrement operations, which actually perform addition,
+not subtraction, by using two's complement arithmetic in "stages": the first stage (inverting the source operand)
+occurs *before* the addition, and the second stage (incrementing the inverted operand) occurs *after* the addition.
+And it appears to be the result of the *first* stage, not the second, that determines the state of the Auxiliary
+Carry flag (AF).
+
+The behavior of the Auxiliary Carry flag (AF) and the associated DAA instruction are probably the most significant
+(and least understood) *arithmetic* differences between the 8080 and all later x86-based CPUs. Well, there's also
+the fact that the 8080 doesn't provide an Overflow flag (OF). Internally however, PC8080 retains the ability to
+calculate overflow (since PC8080 was a fork of PCjs), which should be useful when we add Z80 support to PC8080.
+
+On a related note, [Ken Shirriff](http://www.righto.com/) has some fascinating blog posts on the 8085 that also
+provide clues as to how the 8080 likely operates:
+
+* [Inside the ALU of the 8085 microprocessor (January 2013)](http://www.righto.com/2013/01/inside-alu-of-8085-microprocessor.html)
+* [Silicon reverse engineering: The 8085's undocumented flags (February 2013)](http://www.righto.com/2013/02/looking-at-silicon-to-understanding.html)
+* [The 8085's register file reverse engineered (March 2013)](http://www.righto.com/2013/03/register-file-8085.html)
+* [Reverse-engineering the 8085's ALU and its hidden registers (July 2013)](http://www.righto.com/2013/07/reverse-engineering-8085s-alu-and-its.html)
+* [Reverse-engineering the flag circuits in the 8085 processor (July 2013)](http://www.righto.com/2013/07/reverse-engineering-flag-circuits-in.html)
+* [Reverse-engineering the 8085's decimal adjust circuitry (August 2013)](http://www.righto.com/2013/08/reverse-engineering-8085s-decimal.html)
+
+The idea is to make [PC8080](/modules/pc8080/) sufficiently configurable so that it will work with a variety of
+8080-based systems, including those with memory-mapped video displays (like
+[Space Invaders](/devices/pc8080/machine/invaders/)), as well as simpler terminal-based systems, like the CP/M-based
+systems of old.
+
+In fact, as soon as [Space Invaders](/devices/pc8080/machine/invaders/) is working, my next planned adaptation
+is a DEC VT100 terminal emulator (itself an 8080-based machine) which can then be "wired up" to other PCjs machine
+simulations. This will not be yet-another VT100-compatible emulation -- which, like 8080 emulators, has been done to
+death -- but rather a simulation of the original VT100 hardware, building on [Adam Mayer's](https://github.com/phooky)
+work [reverse-engineering the VT100](https://github.com/phooky/VT100-Hax).
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*April 30, 2016*
diff --git a/_posts/2016-05-04-the-sharpening.md b/_posts/2016-05-04-the-sharpening.md
new file mode 100644
index 000000000..c584acb08
--- /dev/null
+++ b/_posts/2016-05-04-the-sharpening.md
@@ -0,0 +1,95 @@
+---
+layout: post
+title: The Sharpening
+date: 2016-05-04 08:00:00
+permalink: /blog/2016/05/04/
+---
+
+This was the week of The Sharpening.
+
+A while back, I updated most of the machines to use higher-resolution "screens". For example, a typical
+[EGA video configuration](/devices/pcx86/video/ibm/ega/1984-09-13/128kb-autolockfs.xml) now specifies a *screenWidth*
+of 1280 and *screenHeight* of 700, dimensions which are exactly twice the standard EGA resolution.
+
+That change had no effect on the machine's operation, but it did improve the machine's appearance, because
+most people are using much higher resolution monitors today, so by using a higher-resolution "screen" (canvas),
+less interpolation is happening when a machine's screen image is scaled up to fill your browser window.
+
+The amount of scaling *also* depends on whether the machine allows itself to be stretched to fill the browser window.
+For example, this [machine](/devices/pcx86/machine/5160/ega/640kb/array/machine.xml) (used by the
+[EGA Machine Array Demo](/devices/pcx86/machine/5160/ega/640kb/array/)) is limited to an overall *width* of 680 pixels,
+no matter how large you make your browser window:
+
+```xml
+
+```
+
+but most machines don't specify a (maximum) overall width, so their screen canvas is allowed to stretch far beyond
+the initial *screenWidth* and *screenHeight*, thanks to some additional CSS settings.
+
+Larger screens mean less interpolation, which is a good thing if you want the screens to look less "fuzzy."
+However, interpolation also happens at a deeper level, because internally, PCjs uses two canvases to move pixels
+from the machine's frame buffer to your browser: the screen canvas, which I've already discussed, and another
+canvas called the *buffer* canvas, where changes to the machine's frame buffer are, um, buffered.
+
+The *buffer* canvas has the same dimensions as the machine's frame buffer, whereas the *screen* canvas
+has generally higher dimensions (as explained above), which your browser may then be stretching to even higher
+dimensions, depending on your monitor resolution and browser size.
+
+The differential between the *buffer* canvas and *screen* canvas is where additional interpolation (fuzziness)
+creeps in. That is where The Sharpening now occurs.
+
+All the browsers I've tested so far (Chrome, Firefox, and Safari) support a
+[Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
+[Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) property named
+[imageSmoothingEnabled](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled),
+which eliminates much of the fuzziness that would occur when copying pixels from the lower-resolution *buffer* canvas
+to the higher-resolution *screen* canvas.
+
+So I've added a new [Video](/docs/pcx86/video/) property named *smoothing* that can be set to "true" or "false",
+and I've set it to "false" for most machines in the project. If *smoothing* is not set, your browser continues to
+use its default interpolation method.
+
+{% include screenshot.html src="/blog/images/si1978-fuzzier.png" width="339" height="388" title="Space Invaders (Fuzzier)" link="/devices/pc8080/machine/invaders/?smoothing=true" %}
+{% include screenshot.html src="/blog/images/si1978-sharper.png" width="339" height="388" title="Space Invaders (Sharper)" link="/devices/pc8080/machine/invaders/?smoothing=false" %}
+
+For some people, this might be a matter of taste, because less fuzziness necessarily means more pixelation (ie, you
+can see individual pixels more clearly), which becomes more noticeable when switching a machine **Full Screen**.
+So I've also added a URL *smoothing* parameter you can use to override a machine's default setting; eg:
+
+ http://www.pcjs.org/devices/pc8080/machine/invaders/?smoothing=true
+
+See for yourself, by clicking on each of the [Space Invaders](/devices/pc8080/machine/invaders/) images above and
+then clicking the **Full Screen** button; both images link to the same machine, but left one enables image smoothing,
+while the right one does not.
+
+Aspect Ratio
+---
+
+The *smoothing* property joins another recent [Video](/docs/pcx86/video/) property, *aspect*, that was added in a
+[release](https://github.com/jeffpar/pcjs/releases/tag/v1.21.5) last month.
+
+To recap, aspect ratio is display width divided by display height, but the choice of aspect ratio is complicated by
+the fact that none of the early IBM video card/monitor combinations (with the exception of the VGA) displayed square
+pixels, and (with the exception of the MDA) they could display text and graphics at a variety of resolutions.
+
+So, for those users who either 1) don't like the aspect ratios that PCjs has chosen, or 2) just want to squeeze or
+stretch a machine's screen a bit more, there is now an *aspect* parameter you can append to the URL of any page
+containing one or more PCjs machines.
+
+For example:
+
+ http://www.pcjs.org/disks/pcx86/dos/ibm/1.00/?aspect=2.0
+
+will modify the height of the machine's screen to conform to the requested aspect ratio of 2.0. The screen should still
+be responsive to any browser resizing while still retaining that aspect ratio.
+
+---
+
+That's all for now. Work continues on the new [PC8080](/modules/pc8080/) emulator and the
+[Space Invaders](/devices/pc8080/machine/invaders/) test machine. More on that later, when it's finished.
+
+Until then, May the 4th be with you!
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*May 4, 2016*
diff --git a/_posts/2016-08-03-the-vt100-terminal.md b/_posts/2016-08-03-the-vt100-terminal.md
new file mode 100644
index 000000000..e7fccb580
--- /dev/null
+++ b/_posts/2016-08-03-the-vt100-terminal.md
@@ -0,0 +1,66 @@
+---
+layout: post
+title: The VT100 Terminal
+date: 2016-08-03 18:00:00
+permalink: /blog/2016/08/03/
+machines:
+ - type: pc8080
+ id: vt100
+ config: /devices/pc8080/machine/vt100/machine.xml
+---
+
+Summer has been filled with distractions, but I've finally begun making headway on a
+[DEC VT100 Terminal](/devices/pc8080/machine/vt100/) simulation.
+
+Unlike other VT100 emulators, this isn't simply an emulation of VT100 protocols. It's a simulation of the VT100's
+8080 CPU, running the original [VT100 Firmware](/devices/pc8080/rom/vt100/) inside the [PC8080](/modules/pc8080/)
+CPU emulator, along with other essential components that the CPU interacts with to control the display, keyboard, and
+serial port.
+
+Most of the VT100's (non-AVO) functionality should be operational now. Once the VT100 "powers on," it should clear
+the screen and briefly display a "WAIT" message as it processes its Non-Volatile RAM (NVR). When you see a blinking cursor,
+it's ready to use. Press the SET-UP key to configure the terminal, just as you would an original VT100 terminal.
+
+For even more fun, check out the [Dual VT100 Terminals](/devices/pc8080/machine/vt100/dual/) demo.
+
+{% include machine.html id="vt100" %}
+
+Function keys are mapped as follows:
+
+- F1: PF1
+- F2: PF2
+- F3: PF3
+- F4: PF4
+- F6: BREAK
+- F7: LINE FEED
+- F8: NO SCROLL
+- F9: SET-UP
+
+From the SET-UP screen, you can press **4** to switch to LOCAL mode and verify local operation of most VT100
+keys. The following keys have special meaning inside SET-UP Mode:
+
+- 0: RESET
+- 2: SET/CLEAR TAB
+- 3: CLEAR ALL TABS
+- 4: ONLINE/LOCAL
+- 5: SET-UP A/B
+- 6: TOGGLE FEATURE
+- 7: TRANSMIT SPEED
+- 8: RECEIVE SPEED
+- 9: 80/132 COLUMNS
+- SHIFT-S: Save SET-UP Features
+- SHIFT-R: Restore SET-UP Features
+
+The initial goal for the simulation is to provide a virtual terminal that can communicate with other PCjs machines
+and provide a realistic serial communication experience, all from the comfort of your web browser.
+
+Eventually, I would also like to recreate a fully commented source-code listing for the [DEC VT100 ROMs](/devices/pc8080/rom/vt100/).
+DEC's [VT100 Technical Manuals](/pubs/dec/vt100/) are excellent, and they provide an enormous amount of hardware detail,
+but they are a bit light on certain programming details, and there are no firmware listings like those those that IBM provided
+in the early IBM PC Technical Reference manuals.
+
+Check out the [VT100 Debugger Configuration](/devices/pc8080/machine/vt100/debugger/) for additional information about
+VT100 internals and links to other technical resources.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*Aug 3, 2016*
diff --git a/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md b/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md
new file mode 100644
index 000000000..3d8e502d7
--- /dev/null
+++ b/_posts/2016-08-19-connecting-an-ibm-pc-to-a-dec-vt100-terminal.md
@@ -0,0 +1,85 @@
+---
+layout: post
+title: Connecting an IBM PC to a DEC VT100 Terminal
+date: 2016-08-19 10:00:00
+permalink: /blog/2016/08/19/
+machines:
+ - id: ibm5170
+ type: pcx86
+ connection: com2->vt100.serialPort
+ config: /devices/pcx86/machine/5170/ega/2048kb/rev3/machine.xml
+ - id: vt100
+ type: pc8080
+ connection: serialPort->ibm5170.com2
+ config: /devices/pc8080/machine/vt100/machine.xml
+---
+
+Now that you've had a chance to play with a standalone [VT100 Terminal](/devices/pc8080/machine/vt100/), not to mention
+[Dual VT100 Terminals](/devices/pc8080/machine/vt100/dual/), it's time to take [PCjs Machines](/) to the next level, and
+begin connecting PCs to terminals.
+
+Below you'll find an 80286-based [IBM PC AT](/devices/pcx86/machine/5170/ega/2048kb/rev3/) connected to an 8080-based
+[VT100 Terminal](/devices/pc8080/machine/vt100/) via the PC's **COM2** serial port.
+
+At this point, the connection is *very* thin. The [PCx86](/modules/pcx86/) [SerialPort](/modules/pcx86/lib/serialport.js)
+and [PC8080](/modules/pc8080/) [SerialPort](/modules/pc8080/lib/serialport.js) each export exactly two methods:
+
+- *initConnection()*
+- *receiveData()*
+
+The *receiveData()* method always returns *success*, because both SerialPort components buffer all received data.
+For now, the VT100 SerialPort component avoids overflowing the VT100 firmware's buffers by automatically throttling the flow
+of data whenever the firmware, in desperation, issues an XOFF. This "Auto XOFF" behavior will eventually be superseded by
+additional interfaces that provide conventional RS-232 signal-based flow control.
+
+What can you do with these machines? Click on the PC screen and type:
+
+ DIR > COM2
+
+or any command redirected to COM2. The output should appear on the VT100's screen.
+
+A more interesting example:
+
+ CTTY COM2
+
+redirects all *stdout* (screen) and *stdin* (keyboard) I/O to the VT100. Then click on the VT100 screen to switch focus,
+and you can now interact with the PC via the terminal. The `CTTY CON` command will restore control to the PC.
+
+More useful scenarios include machines running PC-based debuggers that communicate via a serial port, such as the
+OS/2 kernel debugger, Windows debuggers like `WDEB386`, some versions of `SYMDEB`, and so on. PC-to-PC configurations will
+also be possible, enabling live demonstrations of classic communication software packages such as `CROSSTALK`.
+
+I've also made it incredibly easy to put machines like this on any PCjs web page. For this blog post, all I had to do
+was add the following Front Matter to the top of the Markdown file:
+
+ machines:
+ - id: ibm5170
+ type: pcx86
+ connection: com2->vt100.serialPort
+ config: /devices/pcx86/machine/5170/ega/2048kb/rev3/machine.xml
+ - id: vt100
+ type: pc8080
+ connection: serialPort->ibm5170.com2
+ config: /devices/pc8080/machine/vt100/machine.xml
+
+and then embed the machines in the post, each with a single line:
+
+ {% raw %}
+ {% include machine.html id="ibm5170" %}
+ {% include machine.html id="vt100" %}
+ {% endraw %}
+
+For people rolling their own web pages, [the basics](/docs/pcx86/) haven't changed, and adding a serial connection merely
+requires adding a *connection* property (eg, `connection:"com2->vt100.serialPort"`) to the *parms* parameter passed to the
+*embedPCx86()* interface. A similar property (eg, `connection:"serialPort->ibm5170.com2"`) must also be added to the *parms*
+passed to *embedPC8080()*.
+
+For the more adventurous, a [Dual Debugger Configuration](/devices/pcx86/machine/5170/ega/2048kb/rev3/debugger/vt100/)
+is also available.
+
+{% include machine.html id="ibm5170" %}
+
+{% include machine.html id="vt100" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*Aug 19, 2016*
diff --git a/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md b/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md
new file mode 100644
index 000000000..11525b102
--- /dev/null
+++ b/_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md
@@ -0,0 +1,53 @@
+---
+layout: post
+title: Introducing PDPjs, a PDP-11 Emulator
+date: 2016-10-06 15:00:00
+permalink: /blog/2016/10/06/
+machines:
+ - id: test1170
+ type: pdp11
+ debugger: true
+ config: /devices/pdp11/machine/1170/vt100/debugger/machine.xml
+ connection: dl11->vt100.serialPort
+ - id: vt100
+ type: pc8080
+ config: /devices/pc8080/machine/vt100/machine.xml
+ connection: serialPort->test1170.dl11
+---
+
+[PDPjs](/devices/pdp11/machine/) is the newest addition to the PCjs family of emulators, joining PCx86, PC8080, and C1Pjs.
+
+While PDPjs may eventually support a range of DEC PDP machines, my current focus is on the PDP-11, starting with the
+PDP-11/70. From there, I'll work backwards to support other PDP-11 models, such as the PDP-11/45, until I reach the
+beginning of the PDP-11 line: the PDP-11/20.
+
+I'm starting with the top-of-the-line PDP-11/70 largely because the core of the emulator is being adapted from the
+JavaScript [PDP-11/70 Emulator (v1.3)](http://skn.noip.me/pdp11/pdp11.html) written by
+Paul Nankervis, who has generously given permission to use his code in PCjs. Since his emulator is a fully functional
+11/70, it made sense to start there and work backwards, factoring out features as needed.
+
+The code has already undergone a lot of refactoring. Opcodes are now decoded by function tables rather than a single
+switch statement, and every opcode is implemented with a discrete function. Other refactoring includes flag management,
+interrupt management, and device management.
+
+Most of the work remaining is in device management. Like other PCjs emulators, PDPjs has a Bus component,
+[bus.js](/modules/pdp11/lib/bus.js), that allows separate device components to register I/O handlers for specific
+UNIBUS addresses. During the initial port, I moved all of Paul's original device management code into one "catch-all"
+component, [device.js](/modules/pdp11/lib/device.js), which has now been converted to the new I/O registration model.
+
+The first new device component is [serialport.js](/modules/pdp11/lib/serialport.js), which is currently the
+only means PDPjs has of communicating with the outside world. So you can try
+[PDPjs connected to a VT100 Terminal](/devices/pdp11/machine/1170/vt100/), by clicking the "Run" button on the test machine.
+The test machine is running [custom boot code](/apps/pdp11/boot/test/), adapted from boot code written by Paul, but due to the
+lack of other device support, nothing can be booted yet.
+
+Obviously PDPjs is very much a work-in-progress. Before I proceed much farther, I really want to put the CPU through
+some rigorous testing, so I'll be on the lookout for some comprehensive PDP-11 instruction tests. Or I'll write my own,
+and compare results across 1 or 2 other PDP-11 emulators.
+
+{% include machine.html id="test1170" %}
+
+{% include machine.html id="vt100" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*Oct 6, 2016*
diff --git a/_posts/2016-10-21-booting-pdp-11-basic.md b/_posts/2016-10-21-booting-pdp-11-basic.md
new file mode 100644
index 000000000..a9c4f7b80
--- /dev/null
+++ b/_posts/2016-10-21-booting-pdp-11-basic.md
@@ -0,0 +1,34 @@
+---
+layout: post
+title: Booting PDP-11 BASIC
+date: 2016-10-21 23:00:00
+permalink: /blog/2016/10/21/
+machines:
+ - id: test1120
+ type: pdp11
+ debugger: true
+ autoMount: ''
+ config: /devices/pdp11/machine/1120/basic/debugger/machine.xml
+---
+
+[PDPjs](http://www.pcjs.org/devices/pdp11/machine/) can now simulate a PDP-11/20. It was one of the first PDP-11
+models, and since it had no MMU, it was limited to a maximum of 56Kb of RAM (or as DEC would say, 28K words), since
+the top 8Kb (or 4K words) of its 16-bit address space was reserved for UNIBUS devices.
+
+The first PDPjs test of a PDP-11/20 machine was running [PDP-11 BASIC](/apps/pdp11/tapes/basic/), by loading
+it directly into memory from the original paper tape image. This required changes to the [RAM](/modules/pdp11/lib/ram.js)
+component, including a new *loadImage()* interface that understands DEC's paper tape format.
+
+To provide a more "authentic" experience, there is also a new [PC11 Paper Tape](/devices/pdp11/pc11/) component,
+and machines can now be configured to include a virtual paper tape reader, with an [assortment of paper tapes](/apps/pdp11/tapes/)
+ready to be attached.
+
+In the real world, before you could load *any* paper tape, you had to first enter a
+[Bootstrap Loader](/apps/pdp11/boot/bootstrap/). PDPjs has simplified that process by including the Bootstrap Loader as
+just another paper tape image that you can "Load" directly into memory. Once that has been loaded, you can then read any
+other paper tape image, once you "Attach" it to the paper tape reader.
+
+{% include machine.html id="test1120" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*Oct 21, 2016*
diff --git a/_posts/2016-11-08-you-should-have-voted-for-the-pdp-11.md b/_posts/2016-11-08-you-should-have-voted-for-the-pdp-11.md
new file mode 100644
index 000000000..dfbee1d9e
--- /dev/null
+++ b/_posts/2016-11-08-you-should-have-voted-for-the-pdp-11.md
@@ -0,0 +1,69 @@
+---
+layout: post
+title: You Should Have Voted For The PDP-11
+date: 2016-11-08 11:00:00
+permalink: /blog/2016/11/08/
+machines:
+ - id: test1170
+ type: pdp11
+ debugger: true
+ config: /devices/pdp11/machine/1170/panel/debugger/machine.xml
+---
+
+Greetings from an alternate reality where DEC's elegant PDP-11 architecture beat out Intel's gross 8086 architecture,
+and DEC managed to gracefully evolve the 16-bit PDP-11 into powerful 32-bit and 64-bit successors, all while maintaining
+excellent backward compatibility.
+
+Unfortunately, you're stuck in your reality, so you have no idea what I'm talking about. Basically, your ancestors voted
+for the cheapest solution rather than the best solution, and now you have to live with the consequences.
+
+The good news: [PDPjs](/devices/pdp11/machine/1170/panel/debugger/) makes it possible for you to go back in time, and for a moment at least,
+and relive the PDP-11 experience. It's still a somewhat primitive experience, but PDPjs is a work-in-progress, so hang in
+there.
+
+The latest release, v1.30.3, adds the following features:
+
+- Functional [Front Panels](/devices/pdp11/panel/1170/#front-panel-basics) (check out the demo below)
+- ROMs such as DEC's [M9312 ROMs](/devices/pdp11/rom/M9312/) can now be installed
+- Support for DEC's [RL11 Disk Controller](/devices/pdp11/rl11/) has been implemented
+
+To test RL11 support below, then select the "XXDP+ Diagnostics" disk from the "Disk Drive Controls",
+click "Load", and wait for the message:
+
+ Mounted disk "XXDP+ Diagnostics" in drive RL0
+
+Then start the machine (click "Run") and make sure the following prompt has been displayed:
+
+ PDP-11 MONITOR V1.0
+
+ BOOT>
+
+At the prompt, type "BOOT RL0". The following text should appear:
+
+ CHMDLD0 XXDP+ DL MONITOR
+ BOOTED VIA UNIT 0
+ 28K UNIBUS SYSTEM
+
+ ENTER DATE (DD-MMM-YY):
+
+ RESTART ADDR: 152010
+ THIS IS XXDP+. TYPE "H" OR "H/L" FOR HELP.
+
+ .
+
+And that's the extent of my testing, so if you try anything else and it doesn't work, feel free to
+[open an issue](https://github.com/jeffpar/pcjs/issues).
+
+Better yet, fork the [PCjs Project](https://github.com/jeffpar/pcjs), debug the problem yourself, test a fix,
+and then send me a pull request. :-)
+
+Finally, another shout-out to Paul Nankervis, who not only generously gave permission
+to use code from his JavaScript [PDP-11/70 Emulator (v1.4)](http://skn.noip.me/pdp11/pdp11.html), but has also patiently
+answered all my questions.
+
+I'm [@jeffpar](http://twitter.com/jeffpar) and I approve this blog post.
+
+{% include machine.html id="test1170" %}
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*Nov 8, 2016*
diff --git a/_posts/2016-11-13-curious-pdp-11-features.md b/_posts/2016-11-13-curious-pdp-11-features.md
new file mode 100644
index 000000000..6188ad998
--- /dev/null
+++ b/_posts/2016-11-13-curious-pdp-11-features.md
@@ -0,0 +1,94 @@
+---
+layout: post
+title: Curious PDP-11 Features
+date: 2016-11-13 13:00:00
+permalink: /blog/2016/11/13/
+---
+
+Last week, I ran PDPjs through a series of early [PDP-11 Paper Tape Diagnostics](/apps/pdp11/tapes/diags/).
+These tests were originally released in 1970 and are documented in such DEC publications as the
+[MAINDEC USER REFERENCE MANUAL](http://archive.pcjs.org/pubs/dec/pdp11/diags/MAINDEC_User_Reference_Manual_Oct73.pdf)
+(October 1973).
+
+[Tests 1-12](/apps/pdp11/tapes/diags/#tests-1-12) (MAINDEC-11-D0AA-PB through MAINDEC-11-D0LA-PB)
+uncovered a couple of uninteresting bugs in the emulator that affected the ASRB and RORB instructions, both involving some
+incorrect masks. But aside from those hiccups, the tests ran fine. Ditto for [Test 13](/apps/pdp11/tapes/diags/#test-13).
+
+Things got much more interesting in [Test 14](/apps/pdp11/tapes/diags/#test-14). For starters (and as DEC's documentation
+points out), the test will immediately fail (HALT) if run on a PDP-11/40 or PDP-11/45, because it tests the RESERVED
+instruction trap by using the MUL opcode, which was only reserved on early PDP-11's, like the PDP-11/20.
+
+After switching to a [PDP-11/20 configuration](/devices/pdp11/machine/1120/panel/debugger/), Test 14 uncovered a series of
+problems, including:
+
+- The DL11 transmitter must generate an interrupt *immediately* after being enabled
+- When a RESERVED instruction trap occurs with an invalid stack pointer, the RESERVED instruction trap must be immediately
+followed by a stack overflow trap
+- When a hardware interrupt occurs with an invalid stack pointer, the hardware interrupt trap must be immediately followed
+by a stack overflow trap (ie, before the first instruction of the hardware interrupt handler is executed)
+- When a BUS error occurs while fetching an opcode, the address of the opcode must be pushed by the trap handler
+- When a trap handler loads a new PSW that allows a hardware interrupt to be acknowledged, the acknowledgement must be delayed
+by one instruction
+
+And finally, the most interesting bug uncovered by Test 14 involved instructions like this:
+
+ MOV R0,(R0)+
+
+Imagine that R0 contains 1000. PDPjs (as well as several other emulators I tested) would write the value 1000 to address
+1000 after auto-incrementing R0 to 1002.
+
+Unfortunately, that's wrong. Apparently, the PDP-11 performs both source and destination address calculations *before*
+reading and writing the source and destination values. So, in the above example, the value 1002 must be written to address 1000.
+
+I should add that not all emulators get this wrong: [SimH](https://github.com/simh/simh), the gold standard of PDP-11
+emulators, handles it correctly.
+
+Don't confuse this behavior with the order in which the source and destination operands are processed (source operands
+are always decoded first, destination operands next), or with the fact that the auto-increment mode is actually a *post*-
+increment mode, whereas auto-decrement is a *pre*-decrement mode. Those things are also true, but they have nothing to do
+with this bug.
+
+PDPjs resolved the bug by using a special (negative) value to indicate a register source operand, which is then converted
+to the register's current value after both operands have been decoded. Test 14 now passes.
+
+Next up: [Test 15](/apps/pdp11/tapes/diags/#test-15), which uncovered a couple more problems:
+
+- The DLL transmitter must *not* generate an interrupt if it is immediately *disabled* after being *enabled*
+- The (old) RTI instruction needs to manage the Trace flag like the (new) RTT instruction if the machine is a PDP-11/20
+
+Test 15 was written for the earliest PDP-11 models, which didn't have the RTT instruction, so when an RTI instruction
+enabled the Trace flag, action on the flag was deferred until after the next instruction. Newer models moved that deferred
+behavior to the RTT instruction, and changed RTI to act on the Trace flag immediately.
+
+This seems like a regrettable decision. It would have been far better for the new instruction to have the new behavior,
+and to leave the original RTI instruction alone, for maximum backward compatibility. DEC engineers probably regretted not
+having separate RTI and RTT instructions from the beginning, and they probably concluded that very little system software
+would be affected by changing RTI.
+
+Unfortunately, these diagnostics are one of the casualties of that decision, meaning that this diagnostic will *not* run
+successfully on an 11/70. And [Test 15](/apps/pdp11/tapes/diags/#test-15) is not alone in that regard.
+[Test 14](/apps/pdp11/tapes/diags/#test-14) also will not run properly on an 11/70; there again, the problem could have been
+avoided if DEC had decided to designate an opcode as *permanently* RESERVED, and then used that for all future RESERVED
+instruction tests, instead of using opcodes that would become new instructions later (eg, MUL).
+
+The most recent paper tape diagnostic I've run is the [11/70 CPU EXERCISER](/apps/pdp11/tapes/diags/#md-11-1170-cpu-exerciser),
+and after several more bug fixes, PDPjs passes that test now, too. That's something not even
+[SimH](https://github.com/simh/simh) is currently able to do (as of November 2016).
+
+---
+
+It's worth noting that all these diagnostic paper tape images are in the [Absolute Loader](/apps/pdp11/tapes/absloader/) format,
+but unlike tapes like [PDP-11 BASIC](/apps/pdp11/tapes/basic/), they don't include a start address, so you have to read the
+documentation for each test to learn the starting procedure, including any switches that should be set first.
+
+However, PDPjs makes life a bit simpler for you. As noted for other [DEC PDP-11 Tape Images](/apps/pdp11/tapes/), any
+"Absolute Format" tape can be loaded directly into RAM using the machine's "Load" button instead of "Attach", allowing you
+to bypass the usual three-step process of loading the [Bootstrap Loader](/apps/pdp11/boot/bootstrap/) in order to load the
+[Absolute Loader](/apps/pdp11/tapes/absloader/) in order to load the desired tape.
+
+Moreover, PDPjs' JSON-encoded paper tape images support an *exec* property that allows the start address to be explicitly
+set, which will override any start address inside the image. For these diagnostics, I have set the start address (usually 200)
+via the *exec* property.
+
+*[@jeffpar](http://twitter.com/jeffpar)*
+*Nov 13, 2016*