Finished porting the blog posts
This commit is contained in:
parent
535f9c8d6b
commit
78ba582d4e
56 changed files with 383 additions and 131 deletions
56
_posts/2013-11-20-a-blog-thats-not-a-blog.md
Normal file
56
_posts/2013-11-20-a-blog-thats-not-a-blog.md
Normal 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*
|
||||
19
_posts/2014-01-01-new-year-new-directions.md
Normal file
19
_posts/2014-01-01-new-year-new-directions.md
Normal 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*
|
||||
56
_posts/2014-03-30-running-on-azure.md
Normal file
56
_posts/2014-03-30-running-on-azure.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
Sigh. But at least the site is up and fully operational now.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*March 30, 2014*
|
||||
47
_posts/2014-03-31-browser-compatibility-woes.md
Normal file
47
_posts/2014-03-31-browser-compatibility-woes.md
Normal 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).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
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*
|
||||
23
_posts/2014-04-01-the-latest-in-emulator-technology.md
Normal file
23
_posts/2014-04-01-the-latest-in-emulator-technology.md
Normal 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*
|
||||
26
_posts/2014-04-12-whats-new-in-1.13.0.md
Normal file
26
_posts/2014-04-12-whats-new-in-1.13.0.md
Normal 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*
|
||||
154
_posts/2014-04-14-node-express-safari.md
Normal file
154
_posts/2014-04-14-node-express-safari.md
Normal 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*
|
||||
45
_posts/2014-04-30-heading-to-new-york.md
Normal file
45
_posts/2014-04-30-heading-to-new-york.md
Normal 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 you’re feeling overwhelmed by the endless proliferation of responsive grids, adaptive images, HTML boilerplates,
|
||||
CSS frameworks and JavaScript whirligigs then what you need is the HTML9 Responsive Boilerstrap JS.*
|
||||
|
||||
> *To install HTML9 Responsive Boilerstrap JS just “attackclone the grit repo pushmerge, then rubygem the lymphnode js shawarma
|
||||
module — and presto!”*
|
||||
|
||||
> *If you’re wondering what H9RBS.js actually is, well, you can abandon any hopes of one day being hip. But if you must know,
|
||||
H9RBS.js is a “flexible, dependency-free, lightweight, device-agnostic, modular, baked-in, component framework MVC library
|
||||
shoelacestrap to help you kickstart your responsive CSS-based app architecture backbone kitchensink tweetybirds.”*
|
||||
|
||||
Seriously, I'm concerned about how the JavaScript language (rather than the endless procession of frameworks) is going to evolve,
|
||||
and whether it even can. Two examples: at a high-level, we have Microsoft pushing [TypeScript](http://www.typescriptlang.org/),
|
||||
and at a much lower level, we have Mozilla pushing [asm.js](http://asmjs.org). And while I like aspects of both those efforts,
|
||||
I'm relunctant to go very far down either of those paths right now, because I don't want to get suckered. Inevitably, someone will
|
||||
see a *different* shiny object along another fork in the road, and everyone will chase after that instead.
|
||||
|
||||
Will [EmpireJS](http://2014.empirejs.org) answer any of these big questions? It doesn't really matter. I just expect to learn stuff,
|
||||
and learning is fun!
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*April 30, 2014*
|
||||
33
_posts/2014-05-12-chrome-kicks-butt.md
Normal file
33
_posts/2014-05-12-chrome-kicks-butt.md
Normal 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*
|
||||
81
_posts/2014-06-14-halt-and-catch-liar.md
Normal file
81
_posts/2014-06-14-halt-and-catch-liar.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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*
|
||||
18
_posts/2014-06-26-more-under-the-hood-changes.md
Normal file
18
_posts/2014-06-26-more-under-the-hood-changes.md
Normal 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*
|
||||
44
_posts/2014-07-30-ega-support.md
Normal file
44
_posts/2014-07-30-ega-support.md
Normal 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
|
||||
<machine> element.
|
||||
|
||||
The second required XML element is a <rom> 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 <video> element, so that the Video component can load
|
||||
the initial 8x14 and 8x8 fonts from the ROM. Support for dynamic loading of fonts from plane 2 of the EGA's memory
|
||||
will be added later; however, current support works well enough to allow switching from 25-line mode to 43-line mode,
|
||||
which essentially switches from the 8x14 font to the 8x8 font.
|
||||
|
||||
The <video> element also supports a *switches* attribute to specify the type of monitor connected to the EGA;
|
||||
this attribute corresponds to the actual switch settings on the EGA card; our default *switches* setting is "0110",
|
||||
which selects an Enhanced Color Monitor, enabling the EGA's maximum resolution of 640x350.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*July 30, 2014*
|
||||
32
_posts/2014-08-01-pc-tech-journal-1987.md
Normal file
32
_posts/2014-08-01-pc-tech-journal-1987.md
Normal 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.
|
||||
|
||||

|
||||
|
||||
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*
|
||||
51
_posts/2014-08-28-supporting-the-80286.md
Normal file
51
_posts/2014-08-28-supporting-the-80286.md
Normal 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*
|
||||
32
_posts/2014-09-02-minor-fixes-and-additions.md
Normal file
32
_posts/2014-09-02-minor-fixes-and-additions.md
Normal 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*
|
||||
71
_posts/2014-09-13-the-ibm-pc-at-alive-and-booting.md
Normal file
71
_posts/2014-09-13-the-ibm-pc-at-alive-and-booting.md
Normal 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*
|
||||
228
_posts/2014-09-30-pcjs-coding-conventions.md
Normal file
228
_posts/2014-09-30-pcjs-coding-conventions.md
Normal 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*
|
||||
27
_posts/2014-10-12-pcjs-released-on-github.md
Normal file
27
_posts/2014-10-12-pcjs-released-on-github.md
Normal 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*
|
||||
34
_posts/2014-10-13-the-8mhz-ibm-pc-at-5170.md
Normal file
34
_posts/2014-10-13-the-8mhz-ibm-pc-at-5170.md
Normal 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*
|
||||
58
_posts/2014-10-17-release-of-pcjs-v1.15.5.md
Normal file
58
_posts/2014-10-17-release-of-pcjs-v1.15.5.md
Normal 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*
|
||||
25
_posts/2014-10-23-release-of-pcjs-v1.15.6.md
Normal file
25
_posts/2014-10-23-release-of-pcjs-v1.15.6.md
Normal 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*
|
||||
83
_posts/2014-10-26-javascript-negativity.md
Normal file
83
_posts/2014-10-26-javascript-negativity.md
Normal 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 (**~**, **|**, **&**, **^**, **<<**, **>>** and
|
||||
**>>>**) treat their operands as 32-bit integer values and produce 32-bit integer results, and you breathe
|
||||
a sigh of relief.
|
||||
|
||||
But then you start noticing oddities. In C, you can take any 32-bit value, such as -1526726656 (which is equivalent
|
||||
to 0xA5000000), mask it with 0x80808080, and get 0x80000000. However, in JavaScript, you actually get -0x80000000,
|
||||
which, sadly, is not equal to 0x80000000.
|
||||
|
||||
To verify, type the following into any JavaScript REPL (eg, Node):
|
||||
|
||||
> n = -1526726656
|
||||
-1526726656
|
||||
> n &= 0x80808080
|
||||
-2147483648
|
||||
> n == 0x80000000
|
||||
false
|
||||
> n == -0x80000000
|
||||
true
|
||||
|
||||
The sign (bit 31) of every 32-bit result is always extended into the entire 52 "significand" bits of the underlying
|
||||
64-bit float. And it's impossible to simply "mask away" those additional sign bits, thanks to a fundamental
|
||||
restriction of JavaScript bitwise operators: they operate *only* on the low 32 bits.
|
||||
|
||||
With one exception: the unsigned right-shift operator. It does more than simply shift zero bits in from
|
||||
the left; it also zeros all the bits above the sign bit. This means that `n >>> 0`, while leaving the low 32 bits
|
||||
unchanged, also clears the upper bits, resulting in a value that is positive, albeit outside the signed 32-bit range.
|
||||
It is equivalent to adding the 33-bit value 0x100000000 to a negative 32-bit number:
|
||||
|
||||
> n = (n < 0? n + 0x100000000 : n)
|
||||
2147483648
|
||||
> n.toString(16)
|
||||
'80000000'
|
||||
|
||||
These operations work because JavaScript is perfectly capable of representing 0x80000000, or any other 32-bit value,
|
||||
as a positive number, but it must use a floating point value to do so. And be careful, because as soon as you perform
|
||||
*any* bitwise operation on a value with bit 31 set, even an operation as innocuous-looking as:
|
||||
|
||||
> n |= 0
|
||||
-2147483648
|
||||
> n.toString(16)
|
||||
'-80000000'
|
||||
|
||||
the result will be negative again. This is simply how all bitwise operators (except for unsigned right-shift) operate:
|
||||
they truncate the result to a signed 32-bit value.
|
||||
|
||||
This might tempt you to think that the right way to write negative 32-bit constants in hex is to simply precede
|
||||
them with a minus sign. But that would be wrong. For example, if you wrote the constant 0x80000080 as "-0x80000080",
|
||||
JavaScript would treat that as negation of 2147483776, resulting in a value whose low 32 bits are 0x7FFFFF80, not
|
||||
0x80000080.
|
||||
|
||||
The safest way to write a 32-bit constant like 0x80000080 is "0x80000080|0", which will produce -2147483520. If you
|
||||
write all your negative 32-bit constants that way, then you won't have to resort to using either unsigned right-shifts
|
||||
or 33-bit addition, which in turn avoids the use of floating point values.
|
||||
|
||||
To continue the fun, try setting bit 0 of 0x80000000, which should give you 0x80000001:
|
||||
|
||||
> n |= 1
|
||||
-2147483647
|
||||
> n.toString(16)
|
||||
'-7fffffff'
|
||||
|
||||
WTF? Have all the low 32 bits flipped instead?
|
||||
|
||||
Actually, no, this time, I'm pulling your leg. The low 32 bits of the internal value are exactly what you would
|
||||
expect: 0x80000001 (the internal representation is more like 0xFFFFF80000001). But as the
|
||||
[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)
|
||||
explain, for a negative number, toString() returns the positive representation of the number, preceded by a - sign,
|
||||
*not* the "two's complement" of the number.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 26, 2014 (Updated September 8, 2015)*
|
||||
55
_posts/2014-12-04-os2-10.md
Normal file
55
_posts/2014-12-04-os2-10.md
Normal 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*
|
||||
71
_posts/2014-12-05-canvas-performance-and-contenteditable.md
Normal file
71
_posts/2014-12-05-canvas-performance-and-contenteditable.md
Normal 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
|
||||
<[textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)> on top of the Canvas,
|
||||
and now I use the <textarea> to provide all keyboard, mouse, and touch events (and pointer locking,
|
||||
for the handful of browsers that support it).
|
||||
|
||||
That seemed to work well, until I tested Safari on an iPad, where I noticed a blinking cursor in the top
|
||||
left corner of the machine's screen; that is, the top left corner of the transparent <textarea>. I tried
|
||||
all sorts of work-arounds suggested online -- setting the textarea's "color" attribute to "transparent",
|
||||
on the theory that the cursor used the same color, or setting the "cursor" attribute to "none" -- but none
|
||||
of those work-arounds seemed to, um, work.
|
||||
|
||||
I had almost settled on adding iOS detection code, and reverting to the old Canvas input code for iOS only,
|
||||
when I noticed that even a Canvas on iOS displayed a blinking cursor -- it was just slightly less annoying
|
||||
because the cursor was flush with the left edge of the Canvas. More importantly, it was also as tall as
|
||||
the full height of the Canvas.
|
||||
|
||||
At this point, it seemed clear that iOS was trying to display the cursor based on what it believed the
|
||||
line height to be (ie, the full height of the Canvas). So I switched back to the transparent <textarea>
|
||||
again, set its "line-height" attribute to zero, and viola: no more blinking cursor.
|
||||
|
||||
So that, in a nutshell, is why v1.16.2 of PCjs comes one day after v1.16.1: because I happened to noticed
|
||||
that drawing performance in desktop Safari was suffering, and that there was a fairly straightforward solution.
|
||||
|
||||
Safari's behavior should probably be considered a bug, as it's probably doing something it shouldn't,
|
||||
like trying to account for an "invisible" blinking cursor. Chrome certainly doesn't have this problem,
|
||||
so unless I was the only person in the world who used "contenteditable" Canvases, this is probably something
|
||||
Safari will want to fix.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*December 5, 2014*
|
||||
46
_posts/2015-01-17-pcjs-uncompiled.md
Normal file
46
_posts/2015-01-17-pcjs-uncompiled.md
Normal 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)*
|
||||
22
_posts/2015-01-28-new-pcjs-control-panel.md
Normal file
22
_posts/2015-01-28-new-pcjs-control-panel.md
Normal 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*
|
||||
32
_posts/2015-02-22-compaq-deskpro-386.md
Normal file
32
_posts/2015-02-22-compaq-deskpro-386.md
Normal 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*
|
||||
931
_posts/2015-02-23-early-80386-cpus.md
Normal file
931
_posts/2015-02-23-early-80386-cpus.md
Normal 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 "ΣΣ" (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 Microsoft’s
|
||||
DUMPBIN utility from Visual C++, and has been since at least the mid-90s. However, the same opcode was apparently reused
|
||||
for the CMPXCHG instruction on some 80486 processors. The confusion seems to have left a lasting mark: Intel’s opcode
|
||||
charts leave 0x0F 0xA6 unassigned even now. The specific test performed by the Windows kernel is to load EAX and EDX
|
||||
with zero and ECX with 0xFF00. If executing XBTS ECX,EDX does not cause an Invalid Opcode exception and clears ecx to
|
||||
zero (which CMPXCHG ECX,EDX would not), then XBTS is deemed to be supported and the processor is model 1 stepping 0.
|
||||
This case of 80386 processor also was known to Windows 3.10 Enhanced Mode, and was rejected as fatal:
|
||||
|
||||
Windows may not run correctly with the 80386 processor in this computer.
|
||||
|
||||
Upgrade your 80386 processor or start Windows in standard mode by typing
|
||||
WIN /s at the MS-DOS prompt.
|
||||
|
||||
> When string instructions such as MOVSB are repeated because of a REP prefix, each operation is ordinarily interruptible.
|
||||
As Intel says (for REP in the [Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference N-Z](http://www.intel.com/design/processor/manuals/253667.pdf)),
|
||||
this “allows long string operations to proceed without affecting the interrupt response time of the system.” It ordinarily
|
||||
applies also to the Debug exception, such as raised by the processor at the end of executing an instruction for which the TF
|
||||
bit is set in the EFLAGS when the instruction started. Programmers may have noticed this in the real world of assembly-language
|
||||
debugging. If the debugger actually does implement its trace command as a trace, as opposed to setting an INT 3 breakpoint
|
||||
where the instruction is calculated to end, then a two-byte REP MOVSB may take many keystrokes to trace through! That
|
||||
model 1 stepping 1 traces through a REP MOVSB without interruption may be helpful when debugging, but it is surely a defect.
|
||||
|
||||
---
|
||||
|
||||
More examples of problems with early 80386 CPUs are posted in "[The Old New Thing](http://blogs.msdn.com/b/oldnewthing/)"
|
||||
blog. Here are some highlights from "[My, what strange NOPs you have!](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx)",
|
||||
by Raymond Chen:
|
||||
|
||||
> [I]f the instruction following a string operation (such as movs) uses opposite-sized addresses from that in the string
|
||||
instruction (for example, if you performed a movs es:[edi], ds:[esi] followed by a mov ax, [bx]) or if the following
|
||||
instruction accessed an opposite-sized stack (for example, if you performed a movs es:[edi], ds:[esi] on a 16-bit stack,
|
||||
and the next instruction was a push), then the movs instruction would not operate correctly.
|
||||
|
||||
> ...
|
||||
|
||||
> [T]here was one bug that manifested itself in incorrect instruction decoding if a conditional branch instruction
|
||||
had just the right sequence of taken/not-taken history, and the branch instruction was followed immediately by a selector load,
|
||||
and one of the first two instructions at the destination of the branch was itself a jump, call, or return. The easy workaround:
|
||||
Insert a NOP between the branch and the selector load.
|
||||
|
||||
> ...
|
||||
|
||||
> [T]he B1 stepping did not support virtual memory in the first 64KB of memory. Fine, don't use virtual memory there.
|
||||
|
||||
> ...
|
||||
|
||||
> If virtual memory was enabled, if a certain race condition was encountered inside the hardware prefetch, and if you executed
|
||||
a floating point coprocessor instruction that accessed memory at an address in the range 0x800000F8 through 0x800000FF,
|
||||
then the CPU would end up reading from addresses 0x000000F8 through 0x0000000FF instead. This one was easy to work around:
|
||||
Never allocate valid memory at 0x80000xxx.
|
||||
|
||||
---
|
||||
|
||||
### 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")*
|
||||
174
_posts/2015-03-26-javascript-idiosyncrasies.md
Normal file
174
_posts/2015-03-26-javascript-idiosyncrasies.md
Normal 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*
|
||||
153
_posts/2015-04-16-compaq-deskpro-386-update.md
Normal file
153
_posts/2015-04-16-compaq-deskpro-386-update.md
Normal 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*
|
||||
26
_posts/2015-05-20-pc-tech-journal-collection.md
Normal file
26
_posts/2015-05-20-pc-tech-journal-collection.md
Normal 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*
|
||||
|
|
@ -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/
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
|
|
|
|||
Loading…
Reference in a new issue