Finished porting the blog posts

This commit is contained in:
Jeff Parsons 2015-12-10 23:41:59 -08:00
commit 78ba582d4e
56 changed files with 383 additions and 131 deletions

View file

@ -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/pc/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/pc/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*

View file

@ -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*

View file

@ -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,
[keyboard-minimal.xml](/devices/pc/keyboard/keyboard-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:
![Azure Logs](/blog/images/iisnode_logs.png)
Sigh. But at least the site is up and fully operational now.
*[@jeffpar](http://twitter.com/jeffpar)*
*March 30, 2014*

View file

@ -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).
![PCjs in AVD](/blog/images/avd_tablet.png)
---
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*

View file

@ -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.
<iframe width="720" height="512" src="http://bing.com/" style="border-webkit-transform:scale(0.5);-moz-transform-scale(0.5);border:1px solid black;border-radius:15px;overflow:auto;width:100%;background-color:#FAEBD7;"></iframe>
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*

View file

@ -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/pcjs/).
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*

View file

@ -0,0 +1,154 @@
---
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/pc/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 be 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:
/*
* 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;
}
}
}
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*

View file

@ -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 youre 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 youre 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*

View file

@ -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*

View file

@ -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.
[<img src="/blog/images/halt-and-catch-liar-thumbnail.jpg" alt='"Halt and Catch Fire" Scene from Episode 2'/>](/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:
![Googlewhack](/blog/images/googlewhack.jpg)
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*

View file

@ -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/]() directory for old documents, and [/disks/pc/]() 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*

View file

@ -0,0 +1,44 @@
---
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/pc/machine/5160/ega/640kb/win101/) in color. Check out our
[Windows 1.01 "Server Array"](/devices/pc/machine/5160/ega/640kb/array/) demo.
[<img src="/blog/images/win101-array-demo-small.jpg" alt='Windows 1.01 "Server Array" Demo'/>](/blog/images/win101-array-demo.jpg)
EGA support is added to a **machine.xml** file using two XML elements; eg:
<video id="videoEGA" model="ega" memory="0x20000" screenwidth="640" screenheight="350"/>
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
&lt;machine&gt; element.
The second required XML element is a &lt;rom&gt; element to load the EGA ROM; eg:
<rom id="romEGA" addr="0xc0000" size="0x4000" file="/devices/pc/video/ibm-ega.json" notify="videoEGA"/>
The *notify* attribute must match the *id* of the &lt;video&gt; 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 &lt;video&gt; 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*

View file

@ -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.
![PC Tech Journal, Jan 1987](http://static.pcjs.org/pubs/pc/magazines/pctj/PCTJ-1987-01/thumbs/PCTJ-1987-01 1.jpeg "link:/pubs/pc/magazines/pctj/PCTJ-1987-01/:200:260")
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*

View file

@ -0,0 +1,51 @@
---
layout: post
title: Supporting the 80286
date: 2014-08-28 11:00:00
category: 80286
permalink: /blog/2014/08/28/
---
The next milestone for PCjs is complete 80286 emulation. My hope is to have it working by the end of the year.
PCjs 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 PCjs 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 **pc.js**.
Up until now, all PCjs 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:
<computer name="IBM PC AT" buswidth="24"/>
<cpu model="80286"/>
<chipset model="5170"/>
...
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. PCjs takes a
different approach, by attempting to simulate an entire PC as it originally existed. Which is why a PCjs 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 PCjs simulation of a 4.77Mhz IBM PC runs at 4.77Mhz. And a PCjs 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 PCjs 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 PCjs is shooting for.
PCjs 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*

View file

@ -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*

View file

@ -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/pc/machine/5170/ega/640kb/rev1/) finally
boots to a PC-DOS prompt. The configuration uses the original [IBM Model 5170 ROM BIOS](/devices/pc/bios/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://static.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 202.pdf)")
reset the keyboard ("[KBD_RESET](http://static.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 disk controller yet (which
I believe is significantly different from the XT hard disk 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*

View file

@ -0,0 +1,228 @@
---
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:
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:
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**:
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:
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:
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:
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:
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/pcjs/lib/defines.js](/modules/pcjs/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:
obj.prop = true;
instead of:
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*

View file

@ -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 Disk 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*

View file

@ -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/pc/machine/5170/ega/1152kb/rev3/) machine configuration
to the list of [IBM PC Machine Configurations](/devices/pc/machine/), and not surprisingly, the new machine
fails to boot.
This machine uses the 3rd [ROM BIOS](/devices/pc/bios/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*

View file

@ -0,0 +1,58 @@
---
layout: post
title: Release of PCjs v1.15.5
date: 2014-10-17 11:00:00
category: Releases
permalink: /blog/2014/10/17/
---
The [8Mhz IBM PC AT](/devices/pc/machine/5170/ega/1152kb/rev3/) machine configuration boots in
v1.15.5 of PCjs now, 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/pcjs/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/pcjs/lib/chipset.js)).
+ The Floppy Disk 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 Disk 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/pcjs/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*

View file

@ -0,0 +1,25 @@
---
layout: post
title: Release of PCjs v1.15.6
date: 2014-10-23 11:00:00
category: Releases
permalink: /blog/2014/10/23/
---
This is a fairly minor update that fixes a few Floppy Disk Controller (FDC) issues and one CPU emulation bug
that prevented PC-DOS 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 (SETUP Disk 2)" through "PC-DOS 7.00 (SETUP Disk 5)", so your best bet is to boot
from the 1.44Mb "PC-DOS 7.00 (Boot Disk)".
Note that you must also use a fairly new 80286 machine configuration, like this [8Mhz IBM PC AT](/devices/pc/machine/5170/ega/1152kb/rev3/),
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/pc/bios/5170/).
*[@jeffpar](http://twitter.com/jeffpar)*
*October 23, 2014*

View file

@ -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 (**~**, **|**, **&**, **^**, **&lt;&lt;**, **&gt;&gt;** and
**&gt;&gt;&gt;**) 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)*

View file

@ -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/pc/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 Boot Disk](/disks/pc/os2/misc/)". 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").
[<img src="/blog/images/os2-debugger.jpg" alt="OS/2 1.0 With Kernel Debugger"/>](/disks/pc/os2/misc/)
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 Boot Disk" from the assortment of [OS/2 Miscellaneous Disks](/disks/pc/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*

View file

@ -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
&lt;[textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)&gt; on top of the Canvas,
and now I use the &lt;textarea&gt; 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 &lt;textarea&gt;. 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 &lt;textarea&gt;
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*

View file

@ -0,0 +1,46 @@
---
layout: post
title: PCjs Uncompiled
date: 2015-01-17 11:00:00
category: Features
permalink: /blog/2015/01/17/
machines:
- type: pc-dbg
id: at-ega-1152k-rev3
config: /devices/pc/machine/5170/ega/1152kb/rev3/machine.xml
uncompiled: true
---
Most machines on [{{ site.pcjs_domain }}](/) run with a compiled version of PCjs, which is produced
by running PCjs 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 PCjs 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: pc-dbg
id: at-ega-1152k-rev3
config: /devices/pc/machine/5170/ega/1152kb/rev3/machine.xml
uncompiled: true
---
...
{% 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)*

View file

@ -0,0 +1,22 @@
---
layout: post
title: New PCjs Control Panel
date: 2015-01-28 11:00:00
category: Control Panel
permalink: /blog/2015/01/28/
machines:
- type: pc-dbg
id: at-ega-1152k-rev3
config: /devices/pc/machine/5170/ega/1152kb/rev3/backtrack/machine.xml
uncompiled: true
---
A new PCjs 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*

View file

@ -0,0 +1,32 @@
---
layout: post
title: Compaq DeskPro 386
date: 2015-02-22 11:00:00
category: 80386
permalink: /blog/2015/02/22/
machines:
- type: pc-dbg
id: deskpro386
config: /devices/pc/machine/compaq/deskpro386/ega/2048kb/machine.xml
uncompiled: true
---
I finally dumped the [Compaq DeskPro 386/16 ROMs](/devices/pc/bios/compaq/deskpro386/) from the motherboard I bought
on ebay last year, so I'm ready to begin adding 80386 support to PCjs.
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 test configuration is displayed below. It's located at:
> [/devices/pc/machine/compaq/deskpro386/ega/2048kb/](/devices/pc/machine/compaq/deskpro386/ega/2048kb/)
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*

View file

@ -0,0 +1,931 @@
---
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 80386, 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.
I won't make the attempt here, either. Using information from various sources, I'll start with an overview
of the steppings, including how each stepping was externally marked and internally identified, along with lists
of associated errata, then move on to more detailed errata information, based on Intel's own documents.
### Steppings
As the "INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986", section 10.1, explains:
> The contents of EAX depend upon the results of the power-up self test. The self-test may be requested
externally by assertion of BUSY# at the end of RESET. The EAX register holds zero if the 80386 passed
the test. A nonzero value in EAX after self-test indicates that the particular 80386 unit is faulty.
If the self-test is not requested, the contents of EAX after RESET is undefined.
> DX holds a component identifier and revision number after RESET as Figure 10-1 illustrates. DH contains 3,
which indicates an 80386 component. DL contains a unique identifier of the revision level.
But what steppings did Intel produce, and what "revision level" was associated with each stepping?
I've read reports that the first stepping (A0) had serious problems, like an inability to return to real mode
(reminiscent of the 80286). Since it was never released commercially, and we know so little about it, let's move
on to the A1 stepping.
From "Advanced 80386 Programming Techniques" by James L. Turley (C) 1988:
> ### Evolution of the 80386
> #### A1 Stepping
> This is the first revision after the initial (A0) introduction of the 80386 [in late 1985]. The A1
stepping became available around the first of the year in 1986.
> + *Hardware Peculiarity*: Unless pin F13 of the 80386 is connected to the +5V power supply, the 80386
never terminates a memory cycle, hanging the processor.
+ *Loading LDTR with a Null Selector*: If you load the local descriptor table register with a null
selector (0000 through 0003) without performing a memory read immediately afterward, the 80386 behaves
erratically. LDTR can be loaded either as the result of an LLDT instruction or as part of a task-switch
operation if the incoming TSS has a null selector in its LDT field.
+ *Loading Bad Stack Selectors*: The 80386 does not check the privilege level of the desired stack
segment selector during an LSS instruction. Normally, the RPL of the selector and the DPL of the segment
must exactly equal the DPL of the currently executing code segment.
+ *Misaligned Selectors*: If a 16-bit memory operand is loaded into a segment register, the 80386 hangs
if the selector is not word-aligned. This can happen with a MOV instruction and with the LDS, LES, LFS,
LGS, and LSS instructions.
+ *Testing Null Selectors*: The 80386 hangs if an LAR, LSL, VERR, or VERW instruction is used to test
a null selector.
+ *Popping Selectors*: When popping a segment selector from the stack, the 80386 performs all of the
privilege-related checks backward. That is, you are allowed to pop a selector to a segment with more
privilege, but not to one with less privilege.
+ *Indirect FAR JMP to Same Privilege Level*: An indirect JMP instruction is one in which the new segment
selector and offset are stored in memory. If the new code segment is at the same privilege level as the
current code segment, the 80386 does not read the selector portion of the JMP operand. The result is a
NEAR JMP to a new offset within the current code segment.
+ *Spurious Breakpoint Exceptions*: A MOV instruction into or out of the debug registers while hardware
breakpoints are enabled may cause a breakpoint fault to be reported. You should temporarily disable the
hardware breakpoints while reading or writing the debug registers and then execute a JMP instruction
to flush the prefetch queue before reenabling them.
+ *Successive Floating-Point Instructions*: If two floating-point instructions are executed close together,
the 80386 may force the coprocessor to start the second one too soon if the first one did not require any
memory operands.
+ *Bad Floating-Point Instructions*: The execution of certain undefined floating-point instructions causes
the 80386 to hang rather than reporting an invalid instruction fault.
+ *Misaligned Floating-Point Instructions*: If 80287 and/or 80387 instructions are not word-aligned, the
80386 passes the wrong instruction to the coprocessor, causing unpredictable behavior.
+ *Misaligned Descriptor Tables*: All segment descriptors should be dword-aligned, or the 80386 may not
read them correctly.
+ *Incorrect Interrupt Vector*: If a maskable interrupt occurs immediately after the 80386 has executed an
instruction with an 8-bit operand, the interrupt is always assigned a vector number of 0.
+ *Handling Exceptions with 80286 Tasks*: If an exception that pushes an error code is handled through a
task gate to an 80286 task, the 80386 generates a double fault (exception 8) as soon as the 80286 task
switch is completed.
+ *Page Fault During Task Switch*: If the new task uses different page tables than the old task and a page
fault occurs during the task-switch operation, the 80386 hangs instead of reporting the page fault.
+ *Returning from a Nested Task*: If you execute an IRET instruction when the NT (Nested Task) flag in
EFLAGS is set, the 80386 returns to your "parent" task. However, it will neglect to clear NT in the outgoing
task's TSS.
+ *Task-Switch Trap Bit*: When a task switch occurs and the incoming TSS has its task-switch bit set, the
80386 generates a debug fault (exception 1). However, it will neglect to set the BT flag in register DR6
before invoking the exception 1 handler.
+ *Invalid TSS Faults*: When an invalid TSS fault (exception 10) occurs as the result of a task-switch
operation, the 80386 pushes the wrong values for EIP and ESP on the error handler's stack.
+ *Wrong Paging Information During Task Switch*: When a task switch occurs and the new task uses a different
set of page tables than the old task (that is, CR3 in the TSS is different), the 80386 reads the new TSS
descriptor and updates the old TSS using the page tables of the new task.
+ *Masking NMI*: If an NMI is serviced through a task gate, the 80386 neglects to reenable NMI inputs after
the NMI-handling task terminates. The 80386 does not recognize NMI inputs while it is servicing an NMI.
+ *Incorrect Error Code*: If an exception that pushes an error code is handled through a task gate, the
80386 may push an error code of 0 instead of the correct error code.
+ *Not-Present LDT*: If a task switch occurs and the new task's TSS selector selects an LDT descriptor
that is marked no present, the 80386 reports a not-present fault (exception 11), instead of an invalid TSS
(exception 10).
+ *Self-test*: The self-test feature does not work on the A1 stepping of the 80386.
+ *Interlevel Transfers*: If you perform a control transfer to a higher privilege level and the procedure
at that level uses an expand-down stack segment for its stack, as opposed to an expand-up data segment,
the 80386 generates a spurious stack fault (exception 12).
+ *Debug Access Fault*: If an attempt is made to read or write the debug registers while GD is set in DR7,
the 80386 generates a debug fault (exception 1). However, the wrong value for EIP will be pushed onto the
exception handler's stack.
+ *Multiply and Divide Errors*: If the 80386 executes a MUL, IMUL, DIV, or IDIV instruction with a memory
operand and general protection fault (exception 13) occurs, the processor hangs rather than reporting the fault.
+ *Intersegment Transfers*: If the last 2 bytes of a direct FAR JMP or FAR CALL instruction are beyond the
end of the code segment, the 80386 hangs rather than reporting a general protection fault.
+ *Intersegment Transfers II*: If the last 2 bytes of a direct FAR JMP or FAR CALL instruction lie on the
other side of a page boundary, the 80386 hangs, regardless of whether the other page is present or not.
Remember, a page boundary occurs every 4kb.
+ *Bit-Test Instructions*: The bit-test instructions that perform read-modify-write cycles to memory (BTC,
BTR, and BTS) do not work properly unless your memory runs without wait states.
+ *Bit Scan Forward Instruction*: If a BSF instruction is executed with a memory operand, and a general
protection fault (exception 13) or a page fault (exception 14) occurs, the 80386 hangs rather than reporting
the fault.
+ *Accessing CR3, TR6, and TR7*: Reading or writing registers CR3, TR6, or TR7 produces incorrect results
if your memory requires wait states. This is particularly obscure, since these instructions do not reference
memory.
> #### B0 Stepping
> The B0 stepping became available around the middle of 1986. As is the case with earlier steppings, the
"B0" marking can sometimes be found on the outside of the device. If not, the code "S40336" or "S40337" may
appear. Starting with the B0 stepping, the 80386 would leave the component identifier, 3, in DH and the
revision identifier, also 3, in DL [These B0 values need to be independently confirmed, because they are
the same values reported for the B1 stepping. -JP]
> + *Interrupts and Privilege Violations*: If a hardware interrupt occurs immediately before an IOPL-sensitive
instruction when CPL is greater than IOPL, the 80386 behaves erratically. Generally, it will produce spurious
stack faults (exception 12). Note that this occurs only if the IOPL-sensitive instruction was going to fail.
+ *Infinite Page Faults*: If a page fault (exception 14) occurs while the 80386 is attempting to invoke a
page fault handler, it generates another page fault rather than reporting a double fault (exception 8).
+ *Invalid TSS Fault*: If an invalid TSS fault (exception 10) is caused by an attempt to switch to a TSS that
is too small, and the exception is handled through a task gate, the 80386 shuts down.
+ *Invalid TSS Fault II*: If you execute an IRET instruction while the NT (Nested Task) flag is set in EFLAGS,
and the "parent" TSS is too small, the 80386 generates a double fault (exception 8).
+ *Invalid TSS Fault III*: If the previous error occurs and the double fault is handled through a trap gate,
the 80386 shuts down.
+ *Invalid TSS Fault IV*: If the gate descriptor for an invalid TSS fault (exception 10) is bad and an
invalid TSS fault occurs for any reason, the 80386 shuts down.
+ *Tracing a REP MOVS Instruction*: The 80386 does not correctly single-step (trace) repeated string move
instructions. Instead, it generates a debug trap after every *other* iteration of the repeated MOVS instruction.
+ *Breakpointing a REP MOVS Instruction*: If a data breakpoint is enabled and it is hit during a repeated
string move instruction, the 80386 reports it only after an even number of iterations. This gives you a
fifty-fifty chance of learning about your breakpoint one instruction late.
+ *16-Bit and 32-Bit Control Transfers*: Whenever control is transferred from 16-bit code to 32-bit code
through a task gate or through a gate that causes a change in privilege level, the 80386 discards the upper
half of the offset address in EIP. If the destination address was greater than 64Kb, this will cause a
program error.
+ *16-Bit and 32-Bit Control Transfers II*: When an IRET instruction that transfers control to a Virtual
8086 mode task is executed, the 80386 neglects to truncate the offset address on the stack to 16 bits.
+ *16-Bit and 32-Bit Control Transfers III*: If a 32-bit call, trap, or interrupt gate is used to transfer
control from a 16-bit code segment to a 32-bit code segment without changing privilege levels, the 80386
treats the gate as a 16-bit gate.
+ *Mixing Address Sizes*: If a LOOP, MOVS, LODS, STOS, CMPS, INS, or OUTS instruction is followed by an
instruction that uses a different address size, the 80386 may use the incorrect address size when updating
either the loop counter or the string index.
> #### B1 Stepping
> The B1 stepping became available in late 1986. It corrected many of the bugs in earlier versions, but
new ones were either introduced or discovered. The best known of these was the widely publicized multiply
failure discovered in mid-1987. The B1 stepping is identifiable either by the "B1" mark or by the code
"S40343," "S40344," or "S40362." As is the case with the B0 stepping, the B1 revision leaves a binary three
in DL after reset. [Again, I would like to see the B0 values independently confirmed. -JP]
> + *IBTS and XBTS Instructions Removed*: The Insert Bit String (IBTS) and Extract Bit String (XBTS)
instructions were removed from the 80386's instruction set. It was determined that they took up too much
space on the microprocessor and that their functionality could be duplicated with the SHLD and SHRD
instructions. The opcodes 0F A6 and 0F A7 now produce invalid opcode faults.
+ *Multiplication Errors*: Certain 80386 microprocessors produce erroneous results when performing
multiplication. Not all B1 stepping 80386s suffer from this bug. It is aggravated by increases in the
processor's operating frequency, elevations in the ambient temperature, or decreases in the power supply
voltage. This failure is extremely pattern sensitive; certain patterns will produce errors readily, while
others never will. [See sample program below]
+ *Double Page Faults*: The bug that appeared in the B0 stepping regarding page faults that occurred
during page faults has been made a permanent feature of the 80386, with one minor change. If a third page
fault occurs while the processor is servicing the first two, the 80386 shuts down. [See Intel Errata #18 below]
+ *Disabling Page Translation*: The 80386 does not stop translating linear addresses to physical addresses
when paging is disabled. Any page-translation entries that are still in the cache will be used, regardless
of the setting of PG in CR0. To completely disable paging, flush the TLB by clearing CR3.
+ *Page Translation Affects I/O Addresses*: When paging is enabled, the MMU sometimes erroneously translates
I/O addresses above 0FFF as well as memory addresses. Coprocessor references (which appear in the I/O space)
are also affected. The I/O addresses are translated as though they were linear memory addresses, using the
memory translation tables cached in the TLB. If the "linear" I/O address is not in the cache, no translation
will take place; only cached entries produce this effect. [See Intel Errata #10 below]
+ *Page Fault Error Codes*: Under certain circumstances, the 80386 pushes an incorrect error code onto the
page fault handler's stack. [See Intel Errata #9 below]
+ *Four-Gigabyte Code Segments*: If you define a 4Gb code segment (limit = FFFFF, G = 1), the base address
of that segment must be dword-aligned, or the 80386 generates a general protection fault (exception 13)
when it fetches an instruction from the beginning of the segment. This feature is expected to become permanent.
+ *Wrong Loop Counter*: After a REP INS instruction finishes its last iteration, register ECX holds the value
FFFFFFFF instead of 0, if the next instruction after the REP INS references memory. [See Intel Errata #11 below]
+ *LSL Instruction and Stack Pointer*: If the LSL instruction is followed by an instruction that references
the stack, register ESP may become corrupted. [See Intel Errata #14 below]
+ *Not-Present LDT*: If a task switch occurs to a Virtual 8086 mode task and the incoming task's TSS holds
a selector to an LDT descriptor that is marked not present, the 80386 generates a not-present fault
(exception 11) rather than reporting an invalid TSS fault (exception 10). [See Intel Errata #16 below]
+ *Reading from CR3, TR6, or TR7*: If hardware breakpoints are enabled, reading from CR3, TR6, or TR7 may
cause spurious debug faults to be reported. It is recommended that you disable breakpoints and then execute
the MOV instruction followed by a JMP instruction before reenabling breakpoints.
+ *Privilege Checking a Null Selector*: If you perform an LAR, LSL, VERR, or VERw instruction using a null
selector (0000 through 0003), the 80386 actually checks the descriptor in slot 0 of the GDT instead of
always failing. [See Intel Errata #15 below]
+ *Privilege Checking Bad Selectors*: An LAR, LSL, VERR, or VERW instruction that checks an unreachable
selector causes the 80386 to hang unless there is a JMP, CALL, or memory-related instruction already in the
prefetch queue. An unreachable selector is one that either is beyond the limit of its descriptor table
or references a non-existent LDT. The processor will remain hung until it receives an interrupt.
+ *Faulting Floating-Point Instructions*: If the second byte of a floating-point instruction is located
in the first byte of a page that will cause a page fault (either because it is not present or because of an
impending privilege violation), the 80386 hangs. The processor will remain hung until it receives an interrupt.
[See Intel Errata #17 below]
> The following sample program has been calculated to produce the [aforementioned multiplication] error.
An 80386 that fails one or more of these multiply instructions is obviously faulty. However, passing does not
guarantee a perfect part. To their credit, Intel agreed to test all 80386s for a limited time and report on their
success or failure. Since then, all 80386s have been tested before shipping. Those that fail have been marked
"For Sixteen-Bit Software Only." [To be exact: "16 BIT S/W ONLY" -JP] Those that passed have been marked with
a double sigma sign. All 80386s produced after the B1 stepping should be free of this defect.
; Perform various 16-bit and 32-bit multiply operations...
K1 DD 41h ; memory-based constant 1
K2 DD 81h ; memory-based constant 2
MOV EAX,0042E8h ; load EAX with operand
MUL K1 ; EAX = EAX * 41H
CMP EAX,10FCE8h ; check answer
JNE fail ; failure if not equal
MOV EAX,085D00h
MUL K1
CMP EAX,021F9D00h
JNE fail
MOV EAX,042E80000h
MUL K1
CMP EAX,0FCE80000h
JNE fail
CMP EDX,010h
JNE fail
MOV EAX,0417A000h
MUL K2
CMP EAX,0FE7A000h
JNE fail
CMP EDX,0002h
JNE fail
MOV DX,0AB66h
MOV AX,09AE8h
MUL DX
CMP AX,0B070h
JNE fail
MOV DX,0FDF3h
MOV AX,09AE8h
MUL DX
CMP AX,05238h
JNE fail
MOV DX,0B554h
MOV AX,0E8EAh
MUL DX
CMP DX,0A4F9h
JNE fail
MOV DX,0B4C6h
MOV AX,0E8EAh
MUL DX
CMP AX,0ACFCh
JNE fail
CMP DX,0A478h
JNE fail
MOV DX,0B318h
MOV AX,0E8EAh
MUL DX
CMP DX,0A2F1h
JNE fail
MOV DX,0B43Bh
MOV AX,0E8EAh
MUL DX
CMP DX,0A3FAh
JNE fail
---
As for the B0 stepping, the October 15, 1991 issue of PC Magazine confirms what Turley noted above:
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.
Most of the information I have obtained about the 80386 begins with the B1 stepping. From a December
17, 1986 Intel document titled "**80386-B1 STEPPING INFORMATION**":
80386-B1 component identifier readable in DH after reset: 03H
80386-B1 revision identifier readable in DL after reset: 03H
At this time, B1 stepping parts are identified with one of the marks shown
below:
|
ii | ii
|
ii A80386-16 | ii A80386-20
ii S40344 | ii S40362
ii (FPO number) | ii (FPO number)
ii m c i '85 '86 | ii m c i '85 '86
|
----------------------------------- ----------------------------------
|
ii |
|
ii A80386 ES B1 |
ii |
ii |
ii m c i '85 '86 |
|
So, the B1 stepping set DL to 0x03 on reset. This would lead one to believe that the revision number
for a B0 stepping was 0x02, but according to Turley (see above), the B0 and B1 steppings report the *same*
revision; as I noted above, it would be nice to see some independent confirmation.
The 80386 CPU on my Compaq DeskPro 386 "Version 2" System Board is labeled as:
A80386-16
S40344
L8260347
(m)(c)i '85 '86
The "S40344" indicates that it's a B1 stepping, although I'm not currently able to power it and run any tests.
According to [OS/2 Museum](http://os2museum.com) proprietor Michal Necasek, the "L8260347" means:
[I]t was actually manufactured in mid-1988. It took me many years to realize how Intel marked the chips.
The initial 'L' (or whatever number/letter it is) can be ignored and the following 3 digits (826) are the
date code. The first is the last digit of the year.... The last two digits are the week of manufacture,
so week 26 of 1988. Many Intel CPUs actually have 2 or 3 date codes on them, probably related to when the
die was made, when the package was assembled, and when it was tested/binned and stamped. On your 386,
there might be another date code on the bottom side, but if there is, it's not going to be too far apart
from the one on the top and it's going to be slightly older.
Fortunately, my 80386-B1 CPU is also marked with a "&Sigma;&Sigma;" (double sigma), which is how Intel marked
parts that tested safe for 32-bit multiplication. Some 80386 CPUs suffered from a manufacturing defect that
could occasionally result in multiplication errors; defective parts that Intel caught were marked with
"16 BIT S/W ONLY" instead of the double sigma.
The only other information I have about revision levels comes from a March 30, 1987 document titled
"**80386-C0 STEPPING INFORMATION**":
80386-C0 component identifier readable in DH after reset: 03H
80386-C0 revision identifier readable in DL after reset: 04H
However, that document does not indicate how a 80386-C0 part was marked, and I've found very little other
information on the C0 stepping; it may have been quickly superseded by the D0 stepping, which used revision
identifier 0x05. D1 and D2 steppings used identifier 0x08.
### Errata
It's fair to say that early 80386 steppings had a lot of problems. But that's not terribly surprising,
considering the 80386's leap in functionality: a new 32-bit architecture that included paging and virtual
8086 mode, while also maintaining 16-bit protected-mode compatibility with the 80286, not to mention
real-mode compatibility with the 8086.
Here's what the world knew about 80386 problems in the B1 stepping, as of December 17, 1986:
1. Opcode Field Incorrect for FSAVE and FSTENV
**Problem**: If an FSAVE or an FSTENV is executed in REAL mode or in VIRTUAL 8086 mode, the opcode field
stored in memory is incorrect if it should have referred to a coprocessor instruction which transfers
either two bytes or ten bytes from memory to the coprocessor. The instruction and operand linear address
fields are correctly stored. Note that coprocessor error-handling routines are the only routines possibly
affected. Also note that the problem does not occur in PROTECTED mode programs (since no opcode is saved
by FSAVE or FSTENV in that case).
**Workaround**: In REAL mode or in VIRTUAL 8086 mode, the instruction linear address field can be used to
read the opcode from memory. Note that the two bytes fetched need to be swapped to yield the image that
FSAVE and FSTENV normally stores.
2. FSAVE, FRESTOR, FSTENV and FLDENV Anomolies [sic] with Paging
**Problem**: If either of the last two bytes of an FSAVE or an FSTENV operand are for any reason not writeable,
or either of the last two bytes of an FRESTOR or FLDENV are for any reason not readable, the instruction
is not restartable.
**Workaround**: This does not not affect typical systems with reasonably-assigned page access rights.
In an obscure situation where this problem arises, a workaround is to avoid having the operand of these
instructions span a page boundary. This can be accomplished by aligning these operands on any 128-byte boundary.
3. Wraparound Coprocessor Operands
**Problem**: This can affect only situations where a coprocessor operand straddles the limit of a segment
of maximum size (i.e. 0FFFFh for a 16-bit segment or 0FFFFFFFFh for a 32-bit segment) or within 108 bytes of
maximum size, thus wrapping around to offset 0 of the segment. Since a wraparound situation is very abnormal
for a compiler or programmer to create, this does not affect a typical system.
Formally, the 80386 architecture does not permit an operand (coprocessor operands included) to wrap around
the end of a segment. If the user issues such an instruction nonetheless in a Protected Mode system, and
the operand starts and ends in valid, present pages of a segment, BUT spans through an invalid or inaccessible
page, the coprocessor may be put in an indeterminate state. In such cases, an FCLEX or FINIT instruction needs
to be executed before any other coprocessor instruction is issued.
**Workaround**: In Real Mode, this is not a problem since protection is not enabled. In Protected Mode,
this problem is avoided simply by not creating coprocessor operands which wrap around the end of the segment,
or by aligning the base of all segments on page boundaries.
4. IRET to TSS with Limit too Small
**Problem**: If an IRET performs a task switch to a TSS of proper descriptor type but invalid (too small) limit,
a Double Fault (exception 8) will result instead of a Invalid TSS Fault (exception 10) as should result.
Furthermore, if the Double Fault entry in the IDT is a trap gate, a shutdown results. In a related topic,
if the TSS Fault entry in the IDT is invalid for any reason (e.g. bad AR byte), then instead of a Double Fault
(exception 8), a shutdown results.
**Workaround**: A working system, one that creates TSS segments of adequate size to hold the processor state
(44 bytes for the TSS of a 16-bit task, 104 bytes for the TSS of a 32-bit task), will not encounter any problems
here. A working system should also provide a valid gate (interrupt, trap, or task gate) in the IDT for exception 8.
5. Single-Stepping First Iteration of REP MOVS
**Problem**: If a REPeated MOVS instruction is executed when single-stepping is enabled (TF = 1 in EFLAGS register),
a single-step trap (exception 1) is taken every two move steps, but should occur each move step. Also, if a data
breakpoint is hit during a odd iteration number of REP MOVS, the data breakpoint trap is not taken until after the
next even-numbered iteration. If the REP MOVS ends with an odd number of iterations, and single-stepping or data
breakpoints are enabled, then a single-step trap or data breakpoint trap on the final iteration will properly occur
after the final, odd-numbered iteration.
**Workaround**: When using the Trap Flag or data breakpoints with a debugger utility, this minor variation of
REP MOVS must be accepted, unless an effort is made to have the debugger emulate the REP MOVS rather than actually
execute it.
6. Task Switch to Virtual 8086 Mode Doesn't Update Prefetch Limit
**Problem**: When a task switch to Virtual 8086 Mode is performed, the prefetch limit is not updated to become 0FFFFh,
but instead remains at its previous value.
**Workaround**: Use the IRET instruction to transfer to Virtual 8086 Mode. Using IRET is the preferred method for
most instances, especially when the master OS dispatches a Virtual 8086 Mode program, because IRET can cause the
transition without a task switch.
7. Wrong Register Size for String Instructions in Mixed 16/32-bit Addressing Systems
**Problem**: If certain string and loop instructions are followed by instructions that either:
1. use a different address size (that is, if either the string instruction or the following instruction
uses an address size prefix), or
2. reference the stack (e.g. PUSH/POP/CALL/RET) and the "B" bit in the SS descriptor is different from the address
size used by the string instructions,
then one or more of [E]CX, [E]SI, or [E]DI is not updated properly. The size of the register (16 vs. 32) is
taken from the following instruction rather than from the string or loop instruction. This could result in
updating only the lower 16 bits of a 32-bit register, or in updating all 32 bits of a register being used as
16 bits. The instructions (and registers) affected by this are:
MOVS ([E]DI), REP MOVS ([E]SI), STOS ([E]DI), INS ([E]DI), and REP INS ([E]CX).
**Workaround**: No workaround is necessary if all code is 16-bit or if all code is 32-bit. The problem only
occurs if instructions with different address sizes are mixed together, or if a code segment of one size is used
with a stack segment of the other size.
In a system which mixes address sizes, add a NOP after each of the above instructions and ensure that the NOP
has the same address size as the string/loop (i.e., if the string/loop instruction includes an address prefix,
place the same address prefix before the NOP; conversely, if the string/loop instruction does not have an address
prefix, do not place a prefix before the NOP).
8. FAR Jump Located Near Page Boundary in Virtual 8086 Mode Paged Systems
**Problem**: In Virtual 8086 Mode, if a direct FAR jump (opcode EAh) instruction is located at the end of a
page (or within 16 bytes of the end), and the next page is not cached in the TLB, the prefetcher limit is not
set by the FAR jump instruction to the "end" on the new code segment, but rather is left at the "end" of the
old code segment. This can allow execution beyond the end of the new segment without triggering a segment limit
violation. Or it can result in a spurious GP fault if the old and new segments overlap, and a prefetch occurs
beyond the limit of the old segment.
Note that the prefetch limit is checked on the linear address, not by comparing IP to 0FFFFh.
**Workaround**: All existing 8086 programs use only 16-bit addressing, and thus will not execute code at offsets
greater than 0FFFFh from the code segment base. Thus the lack of detection of walking off the end of a code segment
should not impact working 8086 programs.
A workaround to the spurious GP fault, if it occurs, is to simply IRET back to the faulting instruction, since the
IRET will correctly set the prefetch limit. If the fault handler has control of the single-step function, a very
simple workaround is to attempt to single-step the faulting instruction. If the single-step succeeded, the handler
could clear the fault, turn off single-stepping, and IRET. If a GP fault occurred attempting to single-step the
instruction, a "real" GP fault is the cause.
If the fault handler cannot access the single-stepping function, it still can check for "real" GP faults which must
be emulated by the master OS, for example, I/O instructions that need to be emulated, CLI/STI instructions that must
be emulated, etc. If none of these faults are recognized, the fault handler can assume this errata caused the GP fault
and simply IRET back to the instruction.
9. Page Fault Error Code on Stack Not Reliable
**Problem**: When a Page Fault (exception 14) occurs, the 3 defined bits in the error code may be unreliable
if a certain sequence of prefetch is happening at the same time.
**Workaround**: Although the page fault error code pushed onto the page fault handler's stack can be unreliable,
as described, the page fault linear address stored in register CR2 is always correct. The page fault handler should
refer to the page fault linear address in CR2 to access the corresponding page table entry and thereby determine
whether the page fault was due to a page "not present" condition, or to a usage violation.
10. Certain I/O Addresses Incorrect when Paging is Enabled
**Problem**: When Paging is enabled, accessing I/O addresses in the range 00001000h-0000FFFFh (4K through 64K-1)
or accessing coprocessor ports (I/O addresses 800000F8h-800000FFh) as a result of executing coprocessor opcodes,
can generate incorrect I/O addresses if paging is enabled and the corresponding linear memory address is marked
"present" and "dirty."
Furthermore, when paging has been enabled and is then turned off, paging translation continues to occur for memory
or I/O cycles (I/O as described above) to linear addresses still stored in the TLB, but paging does not occur for
linear addresses that result in a TLB miss.
**Workaround**: Unless paging is used, this item is not a problem. If paging is used but all I/O ports are below
00001000h (as in a PC-DOS system), then I/O is no problem.
If paging is used and I/O ports exist in the range 0000l000h-0000FFFFh, then either have the memory pages at those
linear addresses marked "not present" (to avoid having those pages table entries cached in the TLB), or if "present,"
have those pages mapped such that bits 12-15 of the physical address equal bits 12-15 of the linear address.
Alternatively, re-assign any I/O ports in the range 00001000h-0000FFFFh to below 00001000h.
If paging is used and the coprocessor is also used, then have the memory page at linear address 80000xxxh either
marked "not present" (to avoid having that page table entry cached in the TLB), or if "present," have the page
mapped such that bit 31 (the most significant bit) of that page's physical address is a 1.
To completely disable 80386 paging when paging was previously enabled, the 80386 TLB should be flushed immediately
after resetting the~PG bit in CRO. The TLB can be flushed, you recall, by writing a Page Table Directory base address
to register CR3.
11. Wrong ECX Update by REP INS
**Problem**: The ECX register (or CX in case of 16-bit operations) is not updated properly in the case of a
REP INS instruction (INPut string instruction with any REPeat prefix) that is followed by an early-start instruction
(e.g. PUSH, POP or memory reference instructions). After any REP-prefixed instruction, ECX is supposed to be 0 (null).
But in the case of a REP INS instruction, ECX is not updated correctly and is 0FFFFFFFFh (or CX is 0FFFFh in case of
16-bit operations). It should be noted that the REP INS executes the correct number of iterations and EDI (or DI)
is updated properly.
**Workaround**: After a REP INS instruction, do not rely on ECX (or CX) being zero. Hence, a new count (if any)
should be MOVed into ECX, rather than being ADDed into ECX.
12. NMI Doesn't Always Bring Chip Out of Shutdown in Obscure Condition with Paging Enabled
**Problem**: If paging is enabled, and if the IDT gate for the Double Fault handler (the gate for exception 8)
points to the null descriptor slot, descriptor 0, in the GDT (this would be very a strange way to set up a system),
and a TLB miss occurs when accessing the null descriptor slot, the chip enters shutdown as it should in this case.
In this specific case however, an incoming NMI will not be able to bring the 386 out of shutdown. In this specific
case, only reset will bring the 386 out of shutdown.
**Workaround**: Ensure that the IDT gate for the Double Fault Handler has a non-null selectors for CS, and that
SS of the destination level is also non-null.
13. HOLD Input During Protected Mode Interlevel IRET when Paging is Enabled
**Problem**: Under specific situations involving paging and the page privilege bits, the HOLD input, and a RET
or IRET instruction performing an inter-level return to level 3, a problem can develop. These situations can be
avoided by the workarounds given.
The first situation, when the inner level stack (levels 0, 1, and 2) is not dword aligned (or not word aligned
in the case of a 16-bit [I]RET), requires that several conditions occur simultaneously:
1. Paging must be enabled, and the page table and directory entries for the inner level stacks must be marked
as supervisor access only.
2. The software must execute an inter-level RET or IRET to a Protected Mode program at privilege level 3.
An inter-level IRET to Virtual 8086 Mode does not exhibit this problem. An inter-level RET or IRET to level 1
or 2 does not exhibit this problem.
3. The inner level stack must be unaligned to a dword boundary (word boundary for a 16-bit [I]RET).
When the first situation occurs, a page fault (exception 14) occurs spuriously, indicating a page level
protection violation during a "user" level read of the inner level stack.
The second situation, whether or not the inner level stack is dword aligned (or word aligned in the case of a
16-bit [I]RET), also requires that several conditions occur simultaneously:
1. Paging must be enabled, and the page table and directory entries for the inner level stacks must be marked
as supervisor access only.
2. The software must execute an inter-level RET or IRET to a Protected Mode program at privilege level 3.
An inter-level IRET to Virtual 8086 Mode does not exhibit this problem. An inter-level RET or IRET to level 1
or 2 does not exhibit this problem.
3. The bus HOLD input must be asserted during the read, cycle which pops ESP (or SP) off the inner stack as a
result of a RET or IRET instruction.
When the second situation occurs, no exception is generated, but the processor will drive an incorrect physical
address during the read cycle in which SS is popped from the inner level stack.
**Workarounds**: A software workaround to both situations is to mark all pages which contain the inner level
stacks as user readable. This prevents either the first or second situation from occurring. The segmentation
system can be used to prevent user access to the linear addresses containing the inner-level stacks.
A workaround if not using the HOLD input is merely to keep the inner-level stacks aligned.
A hardware workaround if using the HOLD input but not using the software workaround above is the following:
Since the problem occurs during the first cycle after a locked cycle to read the CS descriptor, a hardware
workaround is to prevent a HOLD request from hitting the processor during bus cycle following a LOCKed cycle.
This can be accomplished with a latch that delays the LOCK# signal through a flip-flop clocked by READY# to
gate a HOLD request going into the chip. This will prevent a hold request from getting to the 80386 until after
the completion of the first cycle after a LOCKed cycle. For the hardware workaround to be sufficient, all stacks
must be properly aligned, and BS16# must be tied inactive.
14. Protected Mode LSL Instruction Should not be Followed by PUSH/POP
**Problem**: This item pertains only to Protected Mode. If the Protected Mode LSL instruction (Load Segment
Limit instruction, executable only in Protected Mode) is immediately followed by certain instructions that
perform a stack operation, such as PUSH or POP (see exact list below), the value of the [E]SP register may be
incorrect after the stack operation. Note that stack operations resulting from interrupts or exceptions following
LSL do update [E]SP correctly.
**Workaround**: Do not immediately follow the Protected Mode LSL instruction with any of the following stack
operation instructions: IRET (intra-task), POPA, POPF, POP (mem, reg, seg-reg), RET (intrasegment or intersegment),
CALL (direct intrasegment, direct intersegment, indirect intrasegment via reg), ENTER, PUSHA, PUSHF, PUSH (mem,
reg, seg-reg, immed). Other instructions that operate on the stack (e.g. CALL indirect via memory, and LEAVE) can
be used safely after the Protected Mode LSL. Note that even if a forbidden instruction immediately follows LSL,
[E]SP may still be updated correctly, since this problem is data-dependent and only occurs if the LSL operation
succeeded (i.e. if LSL set the ZF flag).
15. LSL/LAR/VERR/VERW Instructions Malfunction with Null Selector
**Problem**: The Protected Mode instructions LSL, LAR, VERR or VERW executed with a null selector (i.e. bits
15 through 2 of the selector set to zero) as the operand will operate on the descriptor at entry 0 of the GDT
instead of unconditionally clearing the ZF flag.
**Workaround**: The "null descriptor" (i.e. the descriptor at entry 0 of the GDT) should be initialized to all
zeroes. If the "null descriptor" is initialized to all zeroes (i.e. an invalid value), the access made by these
instructions to the "null descriptor" will fail (since these instructions only operate on valid descriptors).
The failure will be reported with ZF cleared, which is the desired behavior when the operand is a null selector.
Note that many systems already have the "null descriptor" in the GDT initialized to zeroes, as is desired for
this workaround.
16. "Not Present" LDT in VM86 Task Raises Wrong Exception
**Problem**: A task switch to a VM86 task that has a "not present" LDT descriptor will cause a Segment Not Present
fault (exception 11) rather than an Invalid TSS fault (exception 10).
**Workaround**: The simplest workaround is to use a NULL selector for the LDT in a VM86 task, since the LDT is
not used when executing in Virtual 86 mode. However, if an interrupt or exception occurs, the processor will switch
out of Virtual 86 mode, into protected mode to handle the interrupt, without switching tasks. Thus, the operating
system should be structured so that all Interrupt and Trap gates active when executing a VM86 task reference segments
in the GDT.
If an LDT must be supplied for a task that executes in Virtual 86 mode, there are several easy workarounds. One
is to ensure that LDT segments are never marked "not present" in their segment descriptors. Paging is not affected
by this errata. LDT segments can be paged out and marked "not present" in their page descriptors in systems which
use paging.
If the operating system must mark the LDT segment descriptor "not present", the "not present" (exception 11)
handler must be able to handle the case of a "not present" LDT during a task switch. The "not present" exception
is reported with the LDT selector as the error code and with the VM bit set to 1 in the EFLAGS image of the caller.
Since a VM86 task cannot normally raise a "not present" fault, the "not present" exception handler can detect this
case by checking if the stored VM bit is set. If so, the fault can be redirected to the TSS Fault handler.
17. Coprocessor Instructions Crossing Page/Segment Boundaries
**Problem**: If the first byte of a coprocessor (ESC) instruction is located on the last byte of a page or segment,
and the second byte is located on a page or segment which would create a fault, then the processor will hang when
it tries to signal the fault. The processor remains stopped until an interrupt, NMI, or RESET occurs. This errata
applies only to coprocessor instructions in systems which use virtual memory.
**Workaround**: In virtual memory systems, the time-slice or watchdog timer provides an easy workaround, since a
timer interrupt will always cause the processor to begin interrupt processing. The timer routine should test the
following conditions to determine if this errata was encountered.
1. The saved CS:EIP must point within 8 bytes of the end of a page.
2. The last byte within the page must contain an ESC opcode.
3. All bytes between the saved CS:EIP and the ESC opcode must contain valid prefix opcodes (segment override 26h,
2Eh, 36h, 3Eh, 64h, 65h, address size override 67h, operand size override 66h).
4. The next page is not present, or not accessable.
If all four conditions are true, then the timer routine can assume this errata was encountered, and signal a page
fault, which will clear the condition. This workaround should be placed in the Operating System, so that applications
programs are unaffected.
18. Double Page Faults Do Not Raise Double Fault Exception
**Problem**: If a second page fault occurs, while the processor is attempting to enter the service routine for the
first, then the processor will invoke the page fault (exception 14) handler a second time, rather than the double
fault (exception 8) handler. A subsequent fault, though, will lead to shutdown.
**Workaround**: No workaround is necessary in a working system.
An errata update dated March 26, 1987, produced internally by IBM rather than Intel, noted two additional
issues:
+ Maximum Sized Segments Need Alignment
+ CR3/TRx Move Corrupts LIP (Linear Instruction Pointer)
As an aside, an 80386-C0 document dated March 30, 1987 lists none of the above errata, suggesting that
the C0 stepping fixed all the above problems -- with the exception of #18, which was apparently reclassified
from errata to "feature":
> Double Page Faults Do Not Raise Double Fault Exception
> Problem: If a second page fault occurs, while the processor is attempting to enter the service routine
for the first, then the processor will invoke the page fault (exception 14) handler a second time, rather
than the double fault (exception 8) handler. A subsequent fault, though, will lead to shutdown.
> Workaround: No workaround is necessary in a working system.
On April 30, 1987, another errata update omitted #18, as well as the two new issues documented by IBM
(which, like #18, Intel must have decided were not significant problems "in a working system"), and added
the following:
+ Breakpoints Malfunction after Reading CR3, TR6, or TR7
+ Return Address Incorrect for Segment Limit Fault during FNINIT
On September 1, 1987, the final 80386-B1 errata update that I'm aware of continued to list previous errata (#1-#17),
plus the two errata from April 30 (#18 and #19), along with the following new errata:
+ VERR/VERW/LAR/LSL Instructions Malfunction with Bad Selector
+ Coprocessor Malfunctions with Paging Enabled
This brought the total number of 80386-B1 errata to 21.
There was also a manufacturing problem that caused 32-bit multiplications to fail on some parts, which Intel
publicly acknowledged in April 1987.
From the San Jose Mercury News, April 11, 1987:
INTEL DISCOVERS A BUG IN ITS NEW 80386 CHIP
Author(s): CHRISTOPHER H. SCHMITT AND JIM BARTIMO, Mercury News
Business Writers Date: April 11, 1987 Section: Business
Intel acknowledged Friday that a bug has cropped up in its new flagship microprocessor chip.
Microprocessors serve as the electronic brains of devices ranging from personal computers to
home appliances.
Intel said the bug in its 80386 microprocessor produces incorrect answers when the chip
performs certain mathematical operations. "When you multiply one 32-bit number by another
32-bit number, you get the error," said Intel spokesman Jim Jarrett. A 32-bit number is one
that utilizes the full data path of the chip.
However, operating system software that would let the chip fully use its 32-bit characteristics
is still being developed, so the problem shouldn't immediately affect personal computer users.
Microsoft Corp. of Redmond, Wash., has announced its intention to deliver in early 1988 a 32-bit
operating system known as OS/2.
Even when using the 32-bit capabilities of the chip, the possibility of an error is "rare,"
Jarrett said.
Spokesman Bruce LeBoss said not all of the 100,000 80386 chips Intel has produced so far are
affected, but he declined to elaborate. Intel is sticking with a previously announced estimate
that it will sell from 500,000 to 1 million 80386s this year.
To address the problem, Intel said it is notifying computer companies and other customers and
extending the chip's warranty from one to two years. Anticipating that it will replace some
80386s already in the field, the company charged off an undisclosed amount to cover the
expense. One analyst estimated the charge at about $7 million.
Intel will test the chips it has already sold to determine which ones have the bug. But it will
not replace the chips for free. Instead, Intel will work with customers on an upgrade program.
Intel said it will be up to individual computer makers to work with consumers who have
purchased machines using the 80386 chip.
The problem may be most acutely felt by Houston-based Compaq Computer, which so far has
been the biggest producer of 80386-based computers. Compaq said it will disclose by early
next week plans for coping with the possibility that versions of its Deskpro 386 computer model
contain flawed chips.
The bug may cause some shortages of the 80386. The problem won't be fixed until July, so the
only guaranteed chips are those that have already been produced and have passed Intel's test.
"Everybody wants perfect chips and we'll have fewer of those to go around for a while," Jarrett
said. Analysts indicated that in the long run, the bug is more likely to be embarrassing than
anything else. "You have a complex chip here," said Daniel Klesken, an analyst for Montgomery
Securities in San Francisco. "One should not be surprised that (problems) happen occasionally."
The problem affected the B1 stepping; it's unknown whether other (earlier) steppings were affected
as well.
What follows is some additional information about early 80386 processors from various online sources.
---
Excerpt from "[CPU Identification by the Windows Kernel](http://www.geoffchappell.com/studies/windows/km/cpu/index.htm)", by Geoff Chappell:
> ### 80386
> 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 Microsofts
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: Intels 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 Developers 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.
---
### Instructions
Here's more information on the 80386 opcodes (IBTS and XBTS) that were removed from the 80386, as of the B1 stepping.
[IBTS (0x0F 0xA7)](http://asm.inightmare.org/opcodelst/index.php?op=IBTS)
Opcode IBTS
CPU: 80386 step A0-B0 only
Type of Instruction: User
Instruction: IBTS base,bitoffset,len,src
Flags Affected: None
CPU mode: RM,PM,VM
IBTS r/m16,AX,CL,r16
IBTS r/m32,EAX,CL,r32
COP (Code of Operation): 0FH A7H
Clocks: IBTS
80386: 12/19
[XBTS (0x0F 0xA6)](http://asm.inightmare.org/opcodelst/index.php?op=XBTS):
Opcode XBTS
CPU: 80386 step A0-B0 only
Type of Instruction: User
Instruction: XBTS dst,base,bitoffset,len
Flags Affected: None
CPU mode: RM,PM,VM
XBTS r16,r/m16,AX,CL
XBTS r32,r/m32,EAX,CL
COP (Code of Operation): 0FH A6H
Clocks: XBTS
80386: 6/13
FYI, PCjs now has limited support for the XBTS (0x0F 0xA6) and IBTS (0x0F 0xA7) instructions in any 80386 machine
configuration file that contains a CPU *stepping* attribute of "A0" through "B0".
By limited, I mean that:
1. The Debugger component can disassemble them
2. The X86CPU component can emulate them (but only for bit indexes 0-31)
I'm content with limited emulation, because it seems unlikely there is much (if any) real-world software that actually
used those short-lived instructions except as a way of discriminating between 80386 steppings. I also couldn't find any
decent specs for exactly how these instructions operated, so the emulation will have to stay limited.
The 0xA6 and 0xA7 opcodes were briefly reused on early 80486 CPUs for the byte and word forms, respectively, of CMPXCHG,
but then those CMPXCHG instructions were moved to opcodes 0xB0 and 0xB1 on later 80486 CPUs.
NOTE: The "PC Magazine Programmer's Technical Reference" claims that CMPXCHG used 0x0F opcodes 0xA6 and 0xA7 only on the
80486 B0 stepping, and that they were moved to 0x0F opcodes 0xB0 and 0xB1 on the "B1" stepping. However, I can't find
any independent confirmation of that.
Most other sources suggest that CMPXCHG used the old XBTS/IBTS opcodes only on 80486 "A" steppings, not any "B" steppings.
The PC Magazine author may have simply confused the 0xB0 and 0xB1 opcodes with the B0 and B1 steppings.
*[@jeffpar](http://twitter.com/jeffpar)*
*February 23, 2015*
*(Updated March 9, 2015 with information from "Advanced 80386 Programming Techniques")*

View file

@ -0,0 +1,174 @@
---
layout: post
title: JavaScript Idiosyncrasies
date: 2015-03-16 11:00:00
category: JavaScript
permalink: /blog/2015/03/16/
---
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:
if (parameter == null) { ... }
whereas strict equality requires more work:
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:
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:
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:
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:
if (parameter == null) { ... }
and obviously if *null* is also a acceptable value, then you should definitely use strict equality:
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:
/**
* 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:
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**:
parseInt(i, 10);
However, a more elegant solution is to use the unary "+" operator to coerce the **string** to a **number**:
+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":
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:
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:
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:
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:
n = (n >>> 31) >>> 2;
Also, it's not quite correct to say that a shift count of zero has *no* effect on a number:
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:
n |= 0; // n is displayed as -2004318072 again
*[@jeffpar](http://twitter.com/jeffpar)*
*March 26, 2015*

View file

@ -0,0 +1,153 @@
---
layout: post
title: Compaq DeskPro 386 Update
date: 2015-04-16 11:00:00
category: 80386
permalink: /blog/2015/04/16/
machines:
- type: pc-dbg
id: deskpro386
config: /devices/pc/machine/compaq/deskpro386/ega/2048kb/machine.xml
uncompiled: true
---
PCjs can now boot the [Compaq DeskPro 386/16 ROM BIOS](/devices/pc/bios/compaq/deskpro386/).
There's still a problem with the hard disk 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/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.nasm) that can be re-assembled
with [NASM](http://www.nasm.us/). The initial process of creating the source code is
explained [here](/devices/pc/bios/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 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, PCjs 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 PCjs 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*

View file

@ -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.
[<img src="http://static.pcjs.org/pubs/pc/magazines/pctj/PCTJ-1983-07/thumbs/PCTJ-1983-07 1.jpeg" width="200" height="260" alt="PC Tech Journal, July-August 1983"/>](/pubs/pc/magazines/pctj/)
Happy reading!
*[@jeffpar](http://twitter.com/jeffpar)*
*May 20, 2015*

View file

@ -1,8 +1,8 @@
---
layout: post
title: Debugging the IBM VGA ROM
date: 2015-06-01 11:00:00
categories: video
title: Debugging the IBM VGA ROM
date: 2015-06-01 11:00:00
category: Video
permalink: /blog/2015/06/01/
---

View file

@ -1,8 +1,8 @@
---
layout: post
title: The Strange Case of the EGA Graphics Scroll Bug
date: 2015-06-05 11:00:00
categories: video
title: The Strange Case of the EGA Graphics Scroll Bug
date: 2015-06-05 11:00:00
category: Video
permalink: /blog/2015/06/05/
---

View file

@ -1,8 +1,8 @@
---
layout: post
title: Windows 95
date: 2015-07-17 11:00:00
categories: windows-95
title: Windows 95
date: 2015-07-17 11:00:00
category: Windows 95
permalink: /blog/2015/07/17/
---

View file

@ -1,8 +1,8 @@
---
layout: post
title: Windows 95 In Your Web Browser
date: 2015-09-21 11:00:00
categories: windows-95 80386
title: Windows 95 In Your Web Browser
date: 2015-09-21 11:00:00
category: Windows 95
permalink: /blog/2015/09/21/
machines:
- type: pc-dbg

View file

@ -1,8 +1,8 @@
---
layout: post
title: Windows 95 and Early 80386 CPUs
date: 2015-10-27 11:00:00
categories: windows-95 80386
title: Windows 95 and Early 80386 CPUs
date: 2015-10-27 11:00:00
categories: ['Windows 95', '80386']
permalink: /blog/2015/10/27/
---

View file

@ -1,8 +1,9 @@
---
layout: post
title: Rebuilding the PCjs Website
date: 2015-12-10 11:03:00
categories: blog
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
@ -67,9 +68,10 @@ the following values, depending on whether you want an IBM PC or Challenger 1P,
- c1p-dbg
and `id` can be any identifier you want to use to embed the machine. 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` file, and `state` to specify a JSON-encoded machine state file if the machine
requires a predefined state.
configuration file if not using the default `machine.xml`; `template` to specify an alternate XSL template file if not
using the default `components.xsl` file; `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, and so this appears at the top of the root [README.md](https://raw.githubusercontent.com/jeffpar/pcjs/gh-pages/README.md):
@ -84,3 +86,6 @@ For example, the PCjs home page contains two machines, and so this appears at th
I will continue to include a Node web server with the PCjs project, but it remains to be seen whether I'll update the
Node components to parse the new Jekyll "Front Matter" that's been added to all the Markdown files, or whether I'll leave
leave the Node support as-is on the old "master" branch, and make the "gh-pages" the new default branch.
*[@jeffpar](http://twitter.com/jeffpar)*
*December 10, 2015*