Pulled the blog posts into the other branches
This commit is contained in:
parent
94049fa0e1
commit
f64b4fe110
53 changed files with 3943 additions and 0 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/pcx86/machine/5150/mda/64kb/ "PCjs:ibm5150")
|
||||
|
||||
The script takes care of the rest, adding the appropriate stylesheets and PCjs scripts automatically.
|
||||
|
||||
I had more grandiose plans, including a command-line prompt written in JavaScript that would allow you to
|
||||
navigate the site exactly as you would an IBM PC hard drive from a "DOS prompt", and I may try something
|
||||
like that later, but don't hold your breath.
|
||||
|
||||
I've tried to improve the organization of all the [Machine Configuration Files](/devices/pcx86/machine/) as well.
|
||||
The variety of configurations was getting out of hand. It's a bit tidier now, but there's still room for
|
||||
improvement.
|
||||
|
||||
My workflow is improving, too. I'm more comfortable with [GitHub](http://github.com) now,
|
||||
and I recently switched from Eclipse to JetBrains' [WebStorm](http://www.jetbrains.com/webstorm) (well,
|
||||
actually [PhpStorm](http://www.jetbrains.com/phpstorm), since it's a superset of WebStorm, although I did start
|
||||
with WebStorm), and the new development environment is feeling pretty good now. I've had zero problems with
|
||||
[JetBrains](http://www.jetbrains.com) products and I'm seriously impressed with their quality and completeness,
|
||||
so I have no qualms about moving from the "free" Eclipse platform to the $99 PhpStorm IDE.
|
||||
|
||||
The nice thing about the new GitHub-centric approach is that it's easy to "push" changes to both the repository
|
||||
and the website. I update one or more **README.md** files, "Commit and Push" from the IDE, then "pull" from GitHub
|
||||
on the web server.
|
||||
|
||||
This so-called blog is more of the same: **README.md** files in a series of folders. The only question now:
|
||||
will this actually evolve into a series...?
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*November 20, 2013*
|
||||
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,
|
||||
[us83-buttons-minimal.xml](/devices/pcx86/keyboard/us83-buttons-minimal.xml) is currently 622 bytes locally, but on the
|
||||
Azure server, the reported size is 632 bytes -- one extra CR for each of the file's 10 lines. After a little more
|
||||
digging, I [learned something new](http://git-scm.com/book/ch7-1.html#Formatting-and-Whitespace) about **Git**: it
|
||||
has a setting called `core.autocrlf` which, for me on OS X, defaults to `input` (meaning "convert CR/LF to LF on commit
|
||||
but do NOT convert LF back to CR/LF on check-out"). But Azure apparently sets this to `true`, causing all LFs to be
|
||||
converted to CR/LF.
|
||||
|
||||
Regarding slashes, even when path components contained only slashes, *path.join()* would return paths with
|
||||
backslashes. And unfortunately, this behavior varies from Node module to module. For example, I use the NPM
|
||||
[glob](https://www.npmjs.org/package/glob) module, and even when the input path to *glob()* contains backslashes,
|
||||
its output paths do not.
|
||||
|
||||
In the process of fixing those portability issues, I also had some trouble getting Azure logging to work as
|
||||
documented. Setting `loggingEnabled: true` in **/IISNode.yml** would generate logs in **/site/wwwroot/iisnode/**,
|
||||
but the logs were numerous and poorly organized.
|
||||
|
||||
And the Azure command-line tool that was *supposed* to enable real-time log-streaming to the console:
|
||||
|
||||
azure site log tail pcjs
|
||||
|
||||
would happily report:
|
||||
|
||||
Welcome, you are now connected to log-streaming service
|
||||
|
||||
but it would NEVER display anything but deployment information. Instead, I had to browse the log files using Azure's
|
||||
"FTP DIAGNOSTIC LOGS" link on the web portal -- which presents the logs as one big, ugly, fragmented mess:
|
||||
|
||||

|
||||
|
||||
Sigh. But at least the site is up and fully operational now.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*March 30, 2014*
|
||||
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/).
|
||||
|
||||
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*
|
||||
156
_posts/2014-04-14-node-express-safari.md
Normal file
156
_posts/2014-04-14-node-express-safari.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
layout: post
|
||||
title: "Node + Express != Safari"
|
||||
date: 2014-04-14 11:00:00
|
||||
category: Browsers
|
||||
permalink: /blog/2014/04/14/
|
||||
---
|
||||
|
||||
There's something very odd going on with between Node+Express and Safari, resulting in blank web pages.
|
||||
Don't believe me? Just ask [Google](https://www.google.com/#q=node+express+safari+blank+page).
|
||||
|
||||
[{{ site.pcjs.domain }}]({{ site.url }}/) contains a lot of XML files that are rendered as web pages using XML
|
||||
stylesheets. And occasionally Safari -- and ONLY Safari -- will render those XML files as blank pages.
|
||||
|
||||
For example, here's the
|
||||
[machine.xml](/devices/pcx86/machine/5150/mda/64kb/machine.xml) file that's also embedded on the
|
||||
[{{ site.pcjs.domain }}]({{ site.url }}/) home page.
|
||||
|
||||
When Safari fetched that XML file from an Apache web server (what I used before switching to Node),
|
||||
the request would look like:
|
||||
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||
Cache-Control: max-age=0
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
|
||||
|
||||
and the response would look like:
|
||||
|
||||
Date: Mon, 14 Apr 2014 22:11:20 GMT
|
||||
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
|
||||
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.24 mod_ssl/2.2.26 OpenSSL/0.9.8y
|
||||
Etag: "37a10b3-492-4f6e2e96b2e80"
|
||||
Content-Type: text/xml
|
||||
Connection: Keep-Alive
|
||||
Accept-Ranges: bytes
|
||||
Keep-Alive: timeout=5, max=100
|
||||
Content-Length: 1170
|
||||
|
||||
with a status code of 200 ("OK"). And no matter how many times I hit Safari's Reload button, the response was the same.
|
||||
|
||||
Now with Node+Express, the same exact request would look like:
|
||||
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
|
||||
|
||||
with a response of:
|
||||
|
||||
Date: Mon, 14 Apr 2014 22:16:42 GMT
|
||||
Etag: "1170-1397354346000"
|
||||
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
|
||||
X-Powered-By: Express
|
||||
Content-Type: application/xml
|
||||
Cache-Control: public, max-age=0
|
||||
Connection: keep-alive
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 1170
|
||||
|
||||
HOWEVER, as soon as I used Safari's Back button to return to the home page, and then pressed the Forward button to return to
|
||||
the XML file, the XML request changed to:
|
||||
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||
Cache-Control: max-age=0
|
||||
If-None-Match: "1170-1397354346000"
|
||||
If-Modified-Since: Sun, 13 Apr 2014 01:59:06 GMT
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
|
||||
|
||||
with a response of 304 ("Not Modified") and the following response headers:
|
||||
|
||||
Date: Mon, 14 Apr 2014 22:18:26 GMT
|
||||
Cache-Control: public, max-age=0
|
||||
Etag: "1170-1397354346000"
|
||||
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
|
||||
Connection: keep-alive
|
||||
Accept-Ranges: bytes
|
||||
X-Powered-By: Express
|
||||
|
||||
And here's where the "blank page" problem occurs: pressing Safari's Reload button. Again, the request looks the same as before,
|
||||
and the response is still 304 ("Not Modified"), but the page is blank, and the response now looks like:
|
||||
|
||||
Date: Mon, 14 Apr 2014 22:21:15 GMT
|
||||
Cache-Control: public, max-age=0
|
||||
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
|
||||
Connection: keep-alive
|
||||
Accept-Ranges: bytes
|
||||
X-Powered-By: Express
|
||||
Etag: "1170-1397354346000"
|
||||
|
||||
and no matter how many times I press Reload, the response is the same (except for an updated *Date*), and the page is still blank.
|
||||
|
||||
So, here's the kludge I've added to my Express server code, to prevent Safari from displaying blank pages for those XML files:
|
||||
|
||||
{% highlight javascript linenos %}
|
||||
/*
|
||||
* The Safari "blank page" problem continues to plague us. Our first work-around was for directory
|
||||
* "index.html" documents, which we resolved by always sending the document ourselves, along with an
|
||||
* "ok" (200) response, instead of letting next() handle it, which would result in a "not modified"
|
||||
* (304) response.
|
||||
*
|
||||
* However, the problem also extends to any XML files that we serve to an initial Safari request
|
||||
* (eg, the machine.xml and manifest.xml files that we style as web pages). Safari includes
|
||||
* "Cache-Control max-age=0" in the request, and if the response is "Cache-Control public, max-age=0"
|
||||
* along with a 304 response code, Safari may once again display a blank page.
|
||||
*
|
||||
* This problem appears limited to the initial resource request for a particular URL. When these XML
|
||||
* files are requested by Safari while loading another web page, Safari's caching logic is different
|
||||
* (eg, it doesn't include the same "Cache-Control" setting).
|
||||
*/
|
||||
if (sBaseName == "machine.xml" || sBaseName == "manifest.xml") {
|
||||
var sAgent = req.headers['user-agent'];
|
||||
if (sAgent && sAgent.indexOf("Safari/") >= 0 && sAgent.indexOf("Chrome/") < 0 && sAgent.indexOf("OPR/") < 0) {
|
||||
var sCacheControl = req.headers['cache-control'];
|
||||
if (sCacheControl && sCacheControl.indexOf("max-age=0") >= 0) {
|
||||
fs.readFile(sPath, {encoding: "utf8"}, function doneReadFile(err, sData) {
|
||||
if (err) {
|
||||
next(); // alternatively: res.status(404).send("Cannot GET " + req.path);
|
||||
} else {
|
||||
/*
|
||||
* HACK: Express may still modify our response, turning our 200 status code into a 304
|
||||
* and adding an Etag, unless we ALSO change the req.method from "GET" to something else.
|
||||
* Supposedly, we could also use app.disable('etag'), but I'm not sure that would prevent
|
||||
* Express from changing the status code, and I'm tired of testing work-arounds for this
|
||||
* irritating behavior in Safari.
|
||||
*/
|
||||
req.method = "NONE";
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.status(200).send(sData);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
I should add that this problem wasn't limited to XML files. It's a problem for the first resource requested by
|
||||
Safari for any URL on the site (eg, URLs that default to "index.html" files).
|
||||
|
||||
I'm also rather surprised that no one yet seems to have figured out exactly what's going on here between Node+Express
|
||||
and Safari. Or maybe they have, and I haven't been keeping my Node.js config up-to-date. I've tried to avoid changing
|
||||
too many variables.
|
||||
|
||||
Lots of people have run into this problem. For example, on
|
||||
[StackOverflow](http://stackoverflow.com/questions/18811286/nodejs-express-cache-and-304-status-code), someone
|
||||
concluded that the [node-fresh](https://github.com/visionmedia/node-fresh) module should be changed. And for a while,
|
||||
it was changed, until the change was [reverted](https://github.com/visionmedia/node-fresh/issues/8) -- along with a
|
||||
lengthy discussion about why the change was wrong and that this was really a bug in Safari.
|
||||
|
||||
This "blank page" behavior may well be a bug in Safari, but that doesn't mean Express server components can't
|
||||
or shouldn't provide a work-around for that behavior in the meantime. It also seems that some people who decided
|
||||
this was a bug in Safari did not actually reproduce the bug themselves.
|
||||
|
||||
I don't know the right answer, but I do know that the current situation adversely affects users of other Node-powered
|
||||
websites, who will probably get blank pages when they shouldn't, and other developers, who must all discover/debug/work-around
|
||||
this problem on their own.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*April 14, 2014*
|
||||
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-small.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/](/pubs/) directory for old documents, and [/disks/pcx86/](/disks/pcx86/) contains
|
||||
more disk images, with more on the way. I have a TON of old diskette images, and it has taken more time to organize
|
||||
them and create manifests than I would like.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*June 26, 2014*
|
||||
48
_posts/2014-07-30-ega-support.md
Normal file
48
_posts/2014-07-30-ega-support.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
layout: post
|
||||
title: EGA Support
|
||||
date: 2014-07-30 11:00:00
|
||||
category: Video
|
||||
permalink: /blog/2014/07/30/
|
||||
---
|
||||
|
||||
PCjs v1.14.0 now includes basic EGA support. It emulates the EGA hardware well enough to pass the IBM EGA BIOS
|
||||
diagnostics and run [Windows 1.01](/devices/pcx86/machine/5160/ega/640kb/win101/) in color. Check out our
|
||||
[Windows 1.01 "Server Array"](/devices/pcx86/machine/5160/ega/640kb/array/) demo.
|
||||
|
||||
[<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:
|
||||
|
||||
```xml
|
||||
<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:
|
||||
|
||||
```xml
|
||||
<rom id="romEGA" addr="0xc0000" size="0x4000" file="/devices/pcx86/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*
|
||||
53
_posts/2014-08-28-supporting-the-80286.md
Normal file
53
_posts/2014-08-28-supporting-the-80286.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
layout: post
|
||||
title: Supporting the 80286
|
||||
date: 2014-08-28 11:00:00
|
||||
category: 80286
|
||||
permalink: /blog/2014/08/28/
|
||||
---
|
||||
|
||||
The next milestone for PCx86 is complete 80286 emulation. My hope is to have it working by the end of the year.
|
||||
|
||||
PCx86 version 1.15.0 is the first step on the path to full 80286 support. It includes changes to the physical
|
||||
memory manager and separate real-mode and protected-mode address evaluators. The Debugger supports physical
|
||||
addresses (eg, %FE05B is the same as F000:E05B, assuming real-mode operation), along with breakpoint commands that
|
||||
stop execution on port input/output operations. And the ChipSet component now contains "infrastructure" (a
|
||||
fancy way of saying "partial support") for multiple PICs, DMA controllers, the 8042 keyboard controller (including
|
||||
A20 support), and a bit more -- but not much.
|
||||
|
||||
One of the challenges is creating a single "universal" version of PCx86 that can adapt itself to different machine
|
||||
types without impacting performance. There will not be a **pc8088.js** or a **pc80286.js** or whatever. There will
|
||||
only be **pcx86.js**.
|
||||
|
||||
Up until now, all PCx86 machine XML files assumed an 8088 CPU with a 20-bit bus and a model 5150 or 5160 motherboard.
|
||||
But now, a machine XML file can specify:
|
||||
|
||||
```xml
|
||||
<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. PCx86 takes a
|
||||
different approach, by attempting to simulate an entire PC as it originally existed. Which is why a PCx86 simulation
|
||||
of an IBM PC does NOT run at whatever speed your modern PC happens to run in V86-mode or whatever speed your
|
||||
browser's JavaScript engine tops out at.
|
||||
|
||||
No, a PCx86 simulation of a 4.77Mhz IBM PC runs at 4.77Mhz. And a PCx86 simulation of a 6Mhz IBM PC AT will run at
|
||||
6Mhz. If you want to run the simulation faster, you have that option, but that's not the default. And I'm not saying
|
||||
that PCx86 is *exact* -- exactness is an exercise I'm leaving for another day and/or to other developers who are even
|
||||
more obsessive than I am. I'm just saying that original PCs represent the targets that PCx86 is shooting for.
|
||||
|
||||
PCx86 1.15.0 can now load and run the IBM 5170 ROM BIOS up to the first 80286-specific opcode, so it's off and running.
|
||||
Although "running" isn't quite the right metaphor, because the process of bringing a new machine simulation to
|
||||
completion is a *very* long series of baby steps.
|
||||
|
||||
Also, in preparation for this new phase, I recently dug up a variety of old [80286 CPU Documentation](/pubs/pc/reference/intel/80286/)
|
||||
and posted excerpts. I'm sure none of this information is "new" at this point, but it might have some historical interest.
|
||||
|
||||
Enjoy.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*August 28, 2014*
|
||||
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/pcx86/machine/5170/ega/640kb/rev1/debugger/) finally
|
||||
boots to a PC-DOS prompt. The configuration uses the original [IBM Model 5170 ROM BIOS](/devices/pcx86/rom/5170/),
|
||||
dated January 10, 1984.
|
||||
|
||||
Getting through the BIOS "POST" (Power-On Self Test) diagnostics was like running an obstacle course, with various
|
||||
tests derailing the simulation at every turn.
|
||||
|
||||
Sometimes the problems were as simple as missing hardware. For example, I knew that the PC AT contained two DMA
|
||||
controllers, for a total of 8 DMA channels, but what I didn't know (or had forgotten) is that it also contained 16
|
||||
DMA page registers, some of which the BIOS uses as scratch registers. Since DMA page registers are accessed with
|
||||
I/O instructions that work identically in both real-mode and protected-mode, they obviously offer some advantages
|
||||
over RAM, especially when the BIOS hasn't yet tested all the RAM, or determined how much RAM is installed, or set up
|
||||
descriptors that allow the RAM to be accessed from protected-mode.
|
||||
|
||||
Aside from the additional DMA Controller, other major new motherboard components on the PC AT included a second
|
||||
8259 Interrupt Controller, an 8042 Keyboard ("Kitchen Sink") Controller, and an MC146818 Real-Time Clock/CMOS chip.
|
||||
The Keyboard Controller and Real-Time Clock/CMOS components required the most tinkering to pass through the ROM BIOS
|
||||
gauntlet.
|
||||
|
||||
For example, at one point, the BIOS ("[TEST.21](http://archive.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 202.pdf)")
|
||||
reset the keyboard ("[KBD_RESET](http://archive.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 212.pdf)"),
|
||||
which unmasked the keyboard IRQ and waited for an interrupt, using a loop where CX was initialized to zero and then
|
||||
decremented until either CX wrapped around to zero again *or* an interrupt occurred. The "TEST.21" code then assumed
|
||||
that if "KBD_RESET" returned zero in CX, no interrupt had occurred.
|
||||
|
||||
Unfortunately, my Keyboard Controller was a bit too fast: it generated an interrupt as soon as the keyboard IRQ was
|
||||
unmasked; as a result, CX was never decremented, leaving it at zero.
|
||||
|
||||
---
|
||||
|
||||
Most of the effort getting to this point involved adding support for 80286 protected-mode. That work is still far
|
||||
from complete, but getting through multiple real-mode/protected-mode round trips in the BIOS was an important
|
||||
milestone. Some of the work was outside the CPU component, such as A20 support and processor reset via the 8042
|
||||
controller. Work inside the CPU component included:
|
||||
|
||||
- New 80286 general-purpose instructions (eg, ENTER, LEAVE, new PUSH SP behavior, etc)
|
||||
- New protected-mode instructions (eg, ARPL, LGDT, LIDT, LAR, LSL, VERR, VERW, etc)
|
||||
- Protected-mode segment loading, addressing, and fault handling
|
||||
|
||||
Another "feature" I spent considerable time on was ensuring that 80286 protected-mode support did not adversely
|
||||
real-mode performance, so that the PC and PC XT simulations still run (almost) as fast as before. PCjs dynamically
|
||||
reconfigures itself according to the requirements of the processor and platform it's emulating.
|
||||
|
||||
---
|
||||
|
||||
There's still no support for [LOADALL](/pubs/pc/reference/intel/80286/loadall/) or triple-fault resets, nor for
|
||||
call gates or task gates, nor for conforming code segments or expand-down data segments. 80286-specific cycle
|
||||
counts haven't been incorporated yet, either. The list of remaining 80286 features is long.
|
||||
|
||||
And there's plenty of hardware support left to do: I haven't looked at the AT hard drive controller yet (which
|
||||
I believe is significantly different from the XT hard drive controller), and the keyboard *barely* works; the 8042
|
||||
Keyboard Controller and AT keyboard had a number of features that older PC/XT keyboards did not (like LEDs and
|
||||
programmable repeat rate).
|
||||
|
||||
And there are plenty of issues to investigate. For example, PC-DOS is picking up the correct RTC time, but not the
|
||||
date (PCjs initializes the RTC to the browser's current date/time, unless a hard-coded date/time is specified in the
|
||||
machine XML). And diskette I/O seems a bit slow; I'm concerned that the BIOS is spinning its wheels somewhere
|
||||
unnecessarily. And even though the test machine is configured with 640Kb of RAM, the BIOS is reporting only 64Kb.
|
||||
|
||||
Hmmmm.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*September 13, 2014*
|
||||
246
_posts/2014-09-30-pcjs-coding-conventions.md
Normal file
246
_posts/2014-09-30-pcjs-coding-conventions.md
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
---
|
||||
layout: post
|
||||
title: PCjs Coding Conventions
|
||||
date: 2014-09-30 11:00:00
|
||||
category: JavaScript
|
||||
permalink: /blog/2014/09/30/
|
||||
---
|
||||
|
||||
Here are a few highlights of the (evolving) JavaScript coding conventions used in PCjs.
|
||||
|
||||
### Tabs vs. Spaces
|
||||
|
||||
I've configured my IDE ([WebStorm](http://www.jetbrains.com/webstorm/)) to NEVER use tab characters in .js files
|
||||
(spaces only) and to ALWAYS use tab characters in almost every other type of text file. This is largely because
|
||||
when a web browser displays a JavaScript file (either in the main window or in the Developer Tools window), tabs
|
||||
usually screw up the formatting, which I find annoying when I'm debugging. XML files, on the other hand,
|
||||
are usually reformatted by the browser anyway, so in those cases, I opt for smaller files and use real tabs.
|
||||
|
||||
Note that most of the JavaScript delivered by a PCjs production server will have been compiled by Google's
|
||||
Closure Compiler, which completely eliminates all non-essential whitespace, so this is just a development
|
||||
preference, with little to no impact on production files.
|
||||
|
||||
Regardless of the choice of tab character however, I almost always use 4-column tab stops, except in legacy .asm
|
||||
files, where 8-column tab stops were the norm.
|
||||
|
||||
I've noticed that 2-column tab stops have recently become popular, especially in Node projects; NPM, for example,
|
||||
will rewrite package.json files, replacing my 4-column spacing with 2-column spacing. I don't fight that trend -- I
|
||||
just ignore it.
|
||||
|
||||
### Constants
|
||||
|
||||
Property names with all UPPER-CASE letters (with optional numbers and/or underscores) represent constants.
|
||||
|
||||
I originally adopted this rule in part because it's a popular C language convention, but also because it
|
||||
made it easy to write a preprocessing script (see the PCjs Grunt task **prepjs** in /modules/grunts/prepjs/)
|
||||
that replaced all such property references with the corresponding property values and then removed the original
|
||||
property definitions. Of course, this convention also depended on the properties never being modified *or* enumerated.
|
||||
|
||||
I later discovered that Google's Closure Compiler does an excellent job of automatically inlining properties
|
||||
that are never modified or enumerated, so the **prepjs** preprocessing script is no longer used, but I've stuck
|
||||
with the UPPER-CASE convention.
|
||||
|
||||
I don't bother with JSDoc *@const* annotations, because 1) the project contains far too many constants, 2)
|
||||
all the constants are already effectively annotated by virtue of being UPPER-CASE, and 3) there is no noticeable
|
||||
improvement in the Closure Compiler's inlining capability with the addition of *@const*.
|
||||
|
||||
All constants associated with a component are normally attached to the component's constructor; ie, as properties of
|
||||
the constructor. If you think of a JavaScript constructor as a "class', then constants attached to the constructor
|
||||
can be thought of as "class constants".
|
||||
|
||||
For example, the ChipSet component, which manages (among other things) Programmable Interrupt Controllers or PICs,
|
||||
*could* define the constant for an EOI command like this:
|
||||
|
||||
``` javascript
|
||||
ChipSet.EOI = 0x20; // non-specific EOI (end-of-interrupt)
|
||||
```
|
||||
|
||||
but since the EOI command is actually one of a number Operation Command Words (specifically, OCW2), I include an
|
||||
"OCW2_" prefix in the constant name:
|
||||
|
||||
``` javascript
|
||||
ChipSet.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
|
||||
```
|
||||
|
||||
and since I also like to group constants that are associated with a particular register or port, and since I don't
|
||||
want the ChipSet constructor becoming littered with property constants, I first define a constant object; in this
|
||||
case, **PIC_LO**:
|
||||
|
||||
``` javascript
|
||||
ChipSet.PIC_LO = {};
|
||||
ChipSet.PIC_LO.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
|
||||
ChipSet.PIC_LO.OCW2_EOI_SPEC = 0x60; // specific EOI
|
||||
ChipSet.PIC_LO.OCW2_EOI_ROT = 0xA0; // rotate on non-specific EOI
|
||||
ChipSet.PIC_LO.OCW2_EOI_ROTSPEC = 0xE0; // rotate on specific EOI
|
||||
```
|
||||
|
||||
By using fully-qualified property names for each constant, the code has a more C-like appearance (think *#define*)
|
||||
that's also easier to preprocess.
|
||||
|
||||
However, I've gradually switched to the more conventional JavaScript object notation for class constants:
|
||||
|
||||
``` javascript
|
||||
ChipSet.PIC_LO = {
|
||||
OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
|
||||
OCW2_EOI_SPEC: 0x60, // specific EOI
|
||||
OCW2_EOI_ROT: 0xA0, // rotate on non-specific EOI
|
||||
OCW2_EOI_ROTSPEC: 0xE0 // rotate on specific EOI
|
||||
};
|
||||
```
|
||||
|
||||
because, again, the Closure Compiler does an excellent job inlining such constants (or indeed any property that is
|
||||
never modified *or* enumerated).
|
||||
|
||||
### DEBUG vs. RELEASE
|
||||
|
||||
While we're talking about constants, it's important to be aware of constants that are not scoped to
|
||||
any particular component.
|
||||
|
||||
In [/modules/shared/lib/defines.js](/modules/shared/lib/defines.js), **DEBUG** is set to **TRUE**,
|
||||
enabling all debug-only code by default. It is also declared as a *@define* so that the Closure Compiler can
|
||||
override it, setting it to **FALSE** and disabling debug-only code.
|
||||
|
||||
To ensure that debug-only code is not simply *disabled* but also *removed*, the code should be wrapped with:
|
||||
|
||||
``` javascript
|
||||
if (DEBUG) {
|
||||
[code to be removed by the Closure Compiler]
|
||||
}
|
||||
```
|
||||
|
||||
In many cases, the compiler is able to completely remove calls to debug-only class methods; eg:
|
||||
|
||||
``` javascript
|
||||
Component.assert(off >= 0 && off < this.cb);
|
||||
```
|
||||
|
||||
However, calls to debug-only instance methods seem to be more problematic, so all such calls are wrapped; eg:
|
||||
|
||||
``` javascript
|
||||
if (DEBUG) this.log('load("' + sFileURL + '")');
|
||||
```
|
||||
|
||||
There are a number of other important shared constants in [/modules/shared/lib/defines.js](/modules/shared/lib/defines.js)
|
||||
and PCjs-specific constants in [/modules/pcx86/lib/defines.js](/modules/pcx86/lib/defines.js); refer
|
||||
to those files for more information.
|
||||
|
||||
### Braces and Parentheses
|
||||
|
||||
Most opening braces appear at the end of the line containing the associated "if", "while", "for", "switch",
|
||||
"function", etc, preceded by a single space. And most opening parentheses are also preceded by a single space,
|
||||
except when following "function" or a function name, in which case there is NO space.
|
||||
|
||||
There's always the occasional exception. For example, the opening brace of all the top-level (documented)
|
||||
functions in a module may appear on its own line, because the extra whitespace can make the code a bit more
|
||||
readable.
|
||||
|
||||
It's also important to be aware of JavaScript's automatic semicolon insertion feature and the associated danger of
|
||||
putting an opening brace below a *return* statement that wants to return an object literal. As long as you (and
|
||||
your IDE) are aware of that specific danger, there's no need to be dogmatic about opening braces.
|
||||
|
||||
### Variable Names
|
||||
|
||||
I still tend to follow Charles Simonyi's "[Hungarian](http://en.wikipedia.org/wiki/Hungarian_notation)" naming
|
||||
conventions -- or rather, a naming convention loosely inspired by Hungarian.
|
||||
|
||||
For example, if I need a string or numeric variable representing a "thing," I will name it "sThing" if it's a
|
||||
string or "iThing" if it's a number (or possibly "nThings" if it represents a total of Things or "cThings"
|
||||
if it's a counter of Things). If a string or numeric variable has a very short-term use, I'll probably just name
|
||||
it "s" or "i".
|
||||
|
||||
As I mention [below](./#quotation-marks), I still tend to distinguish single characters from strings too,
|
||||
which means I may sometimes prefix character variables with "ch" and character counters with "cch".
|
||||
|
||||
Of course, variable name prefixes like "s" and "n" are irrelevant if you've already given your variables meaningful
|
||||
names like "nameOfPerson" or "numberOfPeople". And that's fine -- I sometimes do that as well. But in general,
|
||||
I still prefer variable names like "sPerson" and "nPeople".
|
||||
|
||||
I don't try to come up with special prefixes for Objects. If there's a Person object, for example, I'll probably
|
||||
use colloquial names like "personHere" or "personThere". I am stricter with Arrays though: I prefix array variables
|
||||
with "a", arrays of strings and numbers with "as" and "ai" (or "an"), arrays of arrays with "aa", etc. As for Arrays
|
||||
of anything else, I usually don't bother with anything more than an "a" prefix.
|
||||
|
||||
### Quotation Marks
|
||||
|
||||
Because of my C background, I prefer to use double-quotes around multi-character strings and single quotes
|
||||
around single-character strings. While the reasons for doing so are largely historical and currently irrelevant,
|
||||
characters are STILL the building blocks of strings, and even the JavaScript String class contains methods that
|
||||
deal with individual characters (eg, charCodeAt() and fromCharCode()). So for any code that deals explicitly with
|
||||
individual characters, I like to reinforce that with single quotes.
|
||||
|
||||
Also, to emphasize that object property names aren't really strings (even though strings can be used as property
|
||||
names), I tend to use single quotes when quoting property names. That does make me somewhat inconsistent with
|
||||
the JSON standard, which insists that property names be double-quoted, but JSON.stringify() takes care of that, so
|
||||
it's not really a problem. Besides, I have a lot of quibbles with the JSON standard, like its "disapproval" of
|
||||
comments and hexadecimal constants, and its failure to faithfully serialize and deserialize uninitialized Array
|
||||
objects, but I'll leave my gripes about JSON for another post.
|
||||
|
||||
Generally speaking, the only time I quote property names is when I have to. I'll use the "dot" syntax; eg:
|
||||
|
||||
``` javascript
|
||||
obj.prop = true;
|
||||
```
|
||||
|
||||
instead of:
|
||||
|
||||
``` javascript
|
||||
obj['prop'] = true;
|
||||
```
|
||||
|
||||
unless the property name doesn't conform to variable name syntax (eg, if it starts with a digit) or if it's a
|
||||
"public" property and therefore I can't risk Google's Closure Compiler "minifying" the property name to something
|
||||
else.
|
||||
|
||||
I break my own quoting rules slightly when dealing with strings that *contain* double-quotes, since it's more readable
|
||||
to put double-quotes inside single-quoted strings than to "escape" every double-quote with a backslash.
|
||||
|
||||
For code that I originally wrote in PHP and later ported to JavaScript, there was a tendency in the original
|
||||
code to always use double-quotes around strings and "escape" double-quotes regardless, and that tendency may linger
|
||||
in code I didn't feel like rewriting much, but the tendency was due more to idiosyncrasies of PHP than any convention
|
||||
of mine; for example:
|
||||
|
||||
- single-quoted PHP strings may not include any escaped characters (except for single-quote and backslash)
|
||||
- single-quoted PHP strings cannot resolve references to string variables (eg, "the value of foo is {$foo}")
|
||||
|
||||
Because of PHP's restrictions on single-quoted strings, I tended to avoid them. However, in JavaScript, those
|
||||
restrictions/features don't exist.
|
||||
|
||||
### JSDoc
|
||||
|
||||
Most of the PCjs code is documented with [JSDoc](http://usejsdoc.org/) annotations -- not
|
||||
because I want to be able to generate documentation (although that's something to think about), but because
|
||||
it's the only way to tell both the Closure Compiler and my IDE exactly what data types are passed around.
|
||||
The goals are to minimize the number of "code inspection" warnings in the IDE and produce warning-free
|
||||
compilations.
|
||||
|
||||
In order to use the Closure Compiler's ADVANCED_OPTIMIZATIONS option and get maximum performance (and maximum
|
||||
"minification", a form of "uglification"), every function and its parameters needs to be fully typed; otherwise,
|
||||
the Compiler generates way too many warnings/errors -- at least, that was the case when I first started using
|
||||
it a couple of years ago.
|
||||
|
||||
I've adopted a zero-tolerance policy for warnings: nothing gets checked in if the Closure Compiler generates even
|
||||
a single warning.
|
||||
|
||||
And finally, speaking of warnings, I've had to tell [WebStorm](http://www.jetbrains.com/webstorm/) to "shut up"
|
||||
about a few:
|
||||
|
||||
- Unfiltered for…in loop
|
||||
- Bitwise operator usage
|
||||
- Comma expressions
|
||||
- loop statement that doesn't loop
|
||||
- “throw” of exception caught locally
|
||||
|
||||
I acknowledge those those features can introduce bugs if you're not careful, so I make sure I'm careful. I don't
|
||||
subscribe to the dogmatic approach that others (eg, the author of JSLint) take about so-called "risky" features.
|
||||
I agree that it's always a good idea to walk to the crosswalk before crossing a street, but I don't agree that it's
|
||||
*never* a good idea to cross in the middle sometimes, too.
|
||||
|
||||
I've also made the following "weak warnings" instead of "warnings":
|
||||
|
||||
- Unused JavaScript / ActionScript local symbol
|
||||
|
||||
because it's a useful warning, but I don't like being penalized for functions that have been "prototyped" a specific
|
||||
way but can't always be implemented exactly as prototyped.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*September 30, 2014*
|
||||
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 Drive controllers, are all
|
||||
just limping along at the moment.
|
||||
|
||||
Longer term, I'm looking forward to building some new tools, including an "IBM PC Configurator" that will take some
|
||||
of the pain out of building your own bootable PCjs machine configurations, and make them easier to share, embed, etc.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 12, 2014*
|
||||
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/pcx86/machine/5170/ega/1152kb/rev3/debugger/) machine configuration
|
||||
to the list of [IBM PC Machine Configurations](/devices/pcx86/machine/), and not surprisingly, the new machine
|
||||
fails to boot.
|
||||
|
||||
This machine uses the 3rd [ROM BIOS](/devices/pcx86/rom/5170/) that IBM released for the PC AT, a revision that
|
||||
included support for 3.5-inch 1.44Mb diskettes -- which will be nice, because I have a number of 1.44Mb diskette
|
||||
images I would like to be able to read in a PCjs machine.
|
||||
|
||||
Since machines with this BIOS also ran at 8Mhz, I've bumped the CPU speed up to 8,000,000 cycles/second.
|
||||
It'll be interesting to see whether this BIOS also increased any of its hard-coded timing delay-loops as a result.
|
||||
|
||||
Anyway, when I enabled ChipSet I/O port messages in the Debugger:
|
||||
|
||||
m chipset on
|
||||
m port on
|
||||
|
||||
I can see that the BIOS Power-On Self Test (POST) progresses nicely until it starts generating lots of port 0x61
|
||||
activity:
|
||||
|
||||
chipset.inPort(0x0061,8042_RWREG): 0x30 at F000:05A8
|
||||
chipset.inPort(0x0061,8042_RWREG): 0x20 at F000:05AE
|
||||
|
||||
I know what I'm going to be doing this afternoon now.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 13, 2014*
|
||||
58
_posts/2014-10-17-improved-support-for-pc-at-machines.md
Normal file
58
_posts/2014-10-17-improved-support-for-pc-at-machines.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
layout: post
|
||||
title: Improved support for PC AT machines
|
||||
date: 2014-10-17 11:00:00
|
||||
category: Releases
|
||||
permalink: /blog/2014/10/17/
|
||||
---
|
||||
|
||||
The [8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/) machine configuration now boots in
|
||||
[PCjs v1.15.5](https://github.com/jeffpar/pcjs/releases/tag/v1.15.5), which includes the following fixes:
|
||||
|
||||
+ The BIOS expects memory refresh to occur roughly every 16us, which I've resolved by tying the state
|
||||
of the refresh bit in port 0x61 to bit 6 of the CPU cycle count (see *in8042RWReg()* in [chipset.js](/modules/pcx86/lib/chipset.js));
|
||||
the original AT BIOS was satisfied with a refresh bit that merely alternated, whereas the new AT BIOS
|
||||
is much more particular about the rate at which that bit changes, since many hard-coded delay-loops have
|
||||
now been replaced with code that waits for a specific number of refresh cycles.
|
||||
|
||||
+ The 8042 Keyboard Controller emulation needed a few more tweaks, mainly with respect to what happens
|
||||
when the keyboard's "clock" line is toggled (see *set8042CmdData()* in [chipset.js](/modules/pcx86/lib/chipset.js)).
|
||||
|
||||
+ The Floppy Drive Controller needed to add support for the "READ ID" command, in order for the BIOS
|
||||
"double-stepping" test to work (double-stepping is required on an 80-track drive when attempting to read
|
||||
a 40-track diskette).
|
||||
|
||||
+ The BIOS Diskette Reset function does something odd after resetting the Floppy Drive Controller: it
|
||||
issues not one but *four* "SENSE INTERRUPT STATUS" commands to the FDC, and expects each response to
|
||||
return an incrementally larger drive number. I found this a bit mystifying, considering that IBM's
|
||||
own FDC/HDC "combo card" supports a maximum of *two* diskette drives. But, there's no point arguing
|
||||
with a BIOS that's almost 30 years old.
|
||||
|
||||
+ The BIOS attempts to detect what its authors must have considered a common problem: the user's failure
|
||||
to run SETUP after installing a second hard drive. So, when the CMOS reports only one hard drive installed,
|
||||
the BIOS probes for a second hard drive anyway, and it does so by simply writing the drive number to the ATC's
|
||||
"DRVHD" register and then immediately reading the "STATUS" register, without issuing any intervening command.
|
||||
It was an easy fix to *outATCDrvHd()* in [hdc.js](/modules/pcx86/lib/hdc.js), but I was surprised
|
||||
to discover that the ATC had this behavior, and now I'm wondering if there are any other I/O operations
|
||||
that must immediately update the "STATUS" register.
|
||||
|
||||
This PCjs release also fixes a problem reported by a user: if you disable **localStorage** support in your
|
||||
browser, previous versions of PCjs would fault. While every browser that supports PCjs also supports
|
||||
**localStorage**, I didn't consider what might happen if a user decided to turn it off.
|
||||
|
||||
The only downside to turning off **localStorage** is that none of your PCjs machines will be able save/restore
|
||||
their state when you leave/return to the page; they will always reboot.
|
||||
|
||||
Browser's don't always refer to the **localStorage** feature by its actual name, either. For example, in
|
||||
Chrome, the setting that enables/disables **localStorage** is hidden under "Advanced Settings" => "Privacy" =>
|
||||
"Content Settings" => "Cookies" => "Allow local data to be set (recommended)". Which is somewhat misleading
|
||||
and a little annoying, because **localStorage** is *not* a **cookie**.
|
||||
|
||||
PCjs *never* sets any cookies. Cookies are bits of data that your browser saves and then automatically sends
|
||||
off to the server every time you make a request. **localStorage** is nothing more than local storage; it is
|
||||
*not* automatically sent anywhere. Granted, a JavaScript application could abuse it and send it out just like a
|
||||
cookie, but PCjs does *not* do that; the only exception is when PCjs detects a problem, and even then, you must
|
||||
first agree to submit your machine's state as part of the bug report.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 17, 2014*
|
||||
27
_posts/2014-10-23-improved-pc-dos-700-support.md
Normal file
27
_posts/2014-10-23-improved-pc-dos-700-support.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
layout: post
|
||||
title: Improved PC-DOS 7.00 Support
|
||||
date: 2014-10-23 11:00:00
|
||||
category: Releases
|
||||
permalink: /blog/2014/10/23/
|
||||
---
|
||||
|
||||
[PCjs v1.15.6](https://github.com/jeffpar/pcjs/releases/tag/v1.15.6) is a fairly minor update that fixes a few
|
||||
Floppy Drive Controller (FDC) issues and one CPU emulation bug that prevented [PC-DOS 7.00](/disks/pcx86/dos/ibm/7.00/)
|
||||
from working properly.
|
||||
|
||||
There are also some Debugger improvements; for example, if you turn on "fdc" and "int" messages in the
|
||||
Debugger using the "m fdc on" and "m int on" commands, all FDC (INT 0x13) software interrupts will be logged,
|
||||
including descriptions and register values.
|
||||
|
||||
PC-DOS 7.00 still can't be setup from its specially-formatted 1.84Mb
|
||||
[XDF](http://www.os2museum.com/wp/the-xdf-diskette-format/) distribution disk images, "PC-DOS 7.00 (Disk 2)"
|
||||
through "PC-DOS 7.00 (Disk 5)", so your best bet is to boot from the 1.44Mb "PC-DOS 7.00 (1.44M Boot)".
|
||||
|
||||
Note that you must also use a fairly new 80286 machine configuration, like this
|
||||
[8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/),
|
||||
in order to use 1.44Mb diskette images; previous models did not support 3.5-inch diskette drives, unless they had been
|
||||
retrofitted with a newer [BIOS](/devices/pcx86/rom/5170/).
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 23, 2014*
|
||||
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)*
|
||||
23
_posts/2014-10-28-limited-support-for-xdf-disk-images.md
Normal file
23
_posts/2014-10-28-limited-support-for-xdf-disk-images.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
layout: post
|
||||
title: Limited Support for XDF Diskettes
|
||||
date: 2014-10-28 11:00:00
|
||||
category: Releases
|
||||
permalink: /blog/2014/10/28/
|
||||
---
|
||||
|
||||
[PCjs v1.15.7](https://github.com/jeffpar/pcjs/releases/tag/v1.15.7) adds support for the
|
||||
[XDF Diskette Format](http://www.os2museum.com/wp/the-xdf-diskette-format/), which was used in
|
||||
[PC-DOS 7.00](/disks/pcx86/dos/ibm/7.00/).
|
||||
|
||||
However, this support is referred to as "fake" XDF support, because it requires using JSON disk images created
|
||||
by DiskDump *without* the experimental "--xdf" option, which is an option that attempts to encode XDF sectors as they
|
||||
existed on the original diskettes (ie, with varying lengths and non-standard sector IDs).
|
||||
|
||||
"Fake" XDF support works by using conventional 80-track disk images with 23 sectors/track. No standard PC floppy disk
|
||||
format ever used 23 sectors/track, but in this case, by distributing the XDF track data across 23 conventional 512-byte
|
||||
sectors, the PC-DOS 7.00 Setup code that reads XDF disks succeeds. This was probably one of several fall-back options
|
||||
built into the PC-DOS XDF code.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 28, 2014*
|
||||
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/pcx86/machine/#model-5170-machine-configurations). This is the culmination
|
||||
of recent work in PCjs to fully emulate the Intel 80286 processor and 16-bit protected-mode, including undocumented
|
||||
features like [LOADALL](/pubs/pc/reference/intel/80286/loadall/) and triple-fault resets.
|
||||
|
||||
For a quick demo, try the [OS/2 1.0 Debugger Disk](/disks/pcx86/os2/misc/1.0/88286/). In a few seconds,
|
||||
you'll see a very rudimentary OS/2 shell (a slimmed-down version of the OS/2 Program Selector) that allows you to
|
||||
start the protected-mode command interpreter ("Start a Program") or the real-mode command interpreter ("command.com").
|
||||
|
||||
[<img src="/blog/images/os2-debugger.jpg" alt="OS/2 1.0 With Kernel Debugger"/>](/disks/pcx86/os2/misc/1.0/88286/)
|
||||
|
||||
As an added bonus, the Model 5170 machines feature two serial ports, with COM1 connected to a simulated serial
|
||||
mouse and COM2 connected to the **Control Panel** output window.
|
||||
|
||||
Once you've booted the [OS/2 1.0 Debugger Disk](/disks/pcx86/os2/misc/1.0/88286/) from the assortment of
|
||||
[OS/2 Prototype Disks](/disks/pcx86/os2/misc/), you can click on the **Control Panel** output window, press Ctrl-C, and
|
||||
find yourself magically transported into the OS/2 Kernel Debugger. The **Control Panel** display is functioning
|
||||
as both the output window for all PCjs messages and PCjs Debugger commands, as well as a serial input/output device
|
||||
(aka "Dumb Terminal") for any software inside the machine communicating via COM2: in this case, the OS/2 Kernel Debugger.
|
||||
|
||||
> SIDEBAR: You can perform similar tricks with DOS in these machines. Boot any DOS disk (version 2.00 and up)
|
||||
and type "CTTY COM2" at the DOS prompt. All DOS input/output will now be routed to the **Control Panel** display.
|
||||
To restore control to the the machine's keyboard and video display, type "CTTY CON".
|
||||
|
||||
Type "?" for a list of all OS/2 Kernel Debugger commands. Type "g" to continue running OS/2. Make sure you type all
|
||||
OS/2 Kernel Debugger commands into the **Control Panel** output window. Commands typed into the input box *beneath*
|
||||
the output window are processed only by the PCjs Debugger.
|
||||
|
||||
When a fault occurs, OS/2 normally displays a "TRAP" message; however, when the Kernel Debugger is running, it
|
||||
intercepts the fault and displays the faulting instruction. But the PCjs Debugger has ultimate control: using
|
||||
the "m fault on" and "m halt on" commands, the PCjs Debugger will display and halt on any fault first. If you want
|
||||
PCjs to deliver the fault to OS/2, single-step over the faulting instruction and then continue.
|
||||
|
||||
There are still a number of known issues running OS/2. For example, when attempting to install OS/2 1.0 from the
|
||||
installation diskette images onto a hard disk image, OS/2 successfully formats the hard disk and copies the files from
|
||||
the first two diskettes, but usually while copying files from either the second or third diskette, the process stops.
|
||||
There's no crash -- it simply stops copying files and never finishes. My best guess at this point is that some
|
||||
interrupts are being dropped.
|
||||
|
||||
Similarly, if a machine running OS/2 1.0 is left unattended for a few minutes, it may stop responding. Again, there's
|
||||
no crash or other indication of a problem. The machine simply appears hung. "Ctrl-Alt-Del" and "Reset" buttons still
|
||||
work.
|
||||
|
||||
The journey continues.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*December 4, 2014*
|
||||
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*
|
||||
48
_posts/2015-01-17-pcjs-uncompiled.md
Normal file
48
_posts/2015-01-17-pcjs-uncompiled.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
layout: post
|
||||
title: PCx86 Uncompiled
|
||||
date: 2015-01-17 11:00:00
|
||||
category: Features
|
||||
permalink: /blog/2015/01/17/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: at-ega-1152k-rev3
|
||||
debugger: true
|
||||
uncompiled: true
|
||||
config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/machine.xml
|
||||
---
|
||||
|
||||
Most PCx86 machines on [{{ site.pcjs.domain }}](/) run with a compiled version of PCx86, which is produced
|
||||
by running the PCx86 JavaScript source code through Google's Closure Compiler, yielding a smaller (minified)
|
||||
version that loads and runs much faster than the original source code.
|
||||
|
||||
However, certain features are disabled in the compiled versions, including a new BACKTRACK feature that
|
||||
makes it possible to track the contents of memory locations and registers back to their source (eg, to a ROM
|
||||
or file location). Once the BACKTRACK feature is finished, it will be folded into the compiled code, but until
|
||||
then, the only way to experiment with it is by running the uncompiled code.
|
||||
|
||||
To make it easier to launch machines with uncompiled code, a PCx86 machine definition can now set `uncompiled`
|
||||
to *true*, overriding the value of `site.pcjs.compiled` in **_config.yml**.
|
||||
|
||||
Here's what a typical Markdown file would look like:
|
||||
|
||||
{% raw %}
|
||||
---
|
||||
...
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: at-ega-1152k-rev3
|
||||
debugger: true
|
||||
uncompiled: true
|
||||
config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/backtrack/machine.xml
|
||||
---
|
||||
...
|
||||
{% include machine.html id="at-ega-1152k-rev3" %}
|
||||
{% endraw %}
|
||||
|
||||
In fact, that's what we've done in the Markdown file you are reading right now.
|
||||
|
||||
{% include machine.html id="at-ega-1152k-rev3" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*January 17, 2015 (Updated December 10, 2015 to reflect the new `uncompiled` property)*
|
||||
23
_posts/2015-01-28-new-pcjs-control-panel.md
Normal file
23
_posts/2015-01-28-new-pcjs-control-panel.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
layout: post
|
||||
title: New PCx86 Control Panel
|
||||
date: 2015-01-28 11:00:00
|
||||
category: Control Panel
|
||||
permalink: /blog/2015/01/28/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: at-ega-1152k-rev3
|
||||
debugger: true
|
||||
uncompiled: true
|
||||
config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/backtrack/machine.xml
|
||||
---
|
||||
|
||||
A new PCx86 Control Panel is under development, featuring a new "Display Panel" that will provide a variety of
|
||||
information about the machine, in real-time, and operate more efficiently than previous DOM-based Control Panels.
|
||||
|
||||
A preview of the layout is shown below. There's not much to see yet, as this is very much a work-in-progress.
|
||||
|
||||
{% include machine.html id="at-ega-1152k-rev3" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*January 28, 2015*
|
||||
30
_posts/2015-02-22-compaq-deskpro-386.md
Normal file
30
_posts/2015-02-22-compaq-deskpro-386.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
layout: post
|
||||
title: COMPAQ DeskPro 386
|
||||
date: 2015-02-22 11:00:00
|
||||
category: 80386
|
||||
permalink: /blog/2015/02/22/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: deskpro386
|
||||
debugger: true
|
||||
uncompiled: true
|
||||
config: /devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/machine.xml
|
||||
---
|
||||
|
||||
I finally dumped the [COMPAQ DeskPro 386/16 ROMs](/devices/pcx86/rom/compaq/deskpro386/) from the motherboard I bought
|
||||
on ebay last year, so I'm ready to begin adding 80386 support to PCx86.
|
||||
|
||||
I'd also like to locate a copy of the "COMPAQ DeskPro 386 Technical Reference Guide, Volumes 1 and 2". It's not hard
|
||||
to find COMPAQ Maintenance and Service guides online, but their Technical Reference guides are much rarer, perhaps because
|
||||
they were expensive ($149) and not many were sold. Anyway, I'm hoping to either borrow or buy a copy, and then scan and
|
||||
post it.
|
||||
|
||||
A [COMPAQ DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/) test configuration is displayed below.
|
||||
The configuration doesn't run, and the debugger can't disassemble 80386-specific code yet, but this is what I will be
|
||||
using to test and debug my changes over the next few months.
|
||||
|
||||
{% include machine.html id="deskpro386" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*February 22, 2015*
|
||||
116
_posts/2015-02-23-early-80386-cpus.md
Normal file
116
_posts/2015-02-23-early-80386-cpus.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
---
|
||||
layout: post
|
||||
title: Early 80386 CPUs
|
||||
date: 2015-02-23 11:00:00
|
||||
category: 80386
|
||||
permalink: /blog/2015/02/23/
|
||||
---
|
||||
|
||||
Assembling a detailed and accurate history of the Intel 80386 CPU, including a complete listing of all the
|
||||
"steppings" (revisions), when they were released, what "errata" (problems) each stepping suffered from, and
|
||||
which of those problems were fixed by a later stepping, seems virtually impossible at this late date.
|
||||
|
||||
See [Intel 80386 CPU Information](/pubs/pc/reference/intel/80386/) for what the PCjs Project has been able to
|
||||
collect about 80386 steppings so far, including how some of them were externally marked and internally identified,
|
||||
along with lists of associated errata, much of it based on Intel's own documents.
|
||||
|
||||
Of course, there's also an eclectic mix of information about early 80386 processors available from various online
|
||||
sources. A few of those are highlighted below.
|
||||
|
||||
---
|
||||
|
||||
Excerpt from "Inside Track", [PC Magazine, February 24, 1987](https://books.google.com/books?id=phxlBt4dX3oC&lpg=PA67&pg=PA67#v=onepage),
|
||||
by John C. Dvorak:
|
||||
|
||||
> **80386 Bug Stopper Dept.**: If you buy an 80386 machine, card, or chip, make sure you **get the B1 revision of
|
||||
the chip** or something newer (B2, B3, and so on). There are **far too many bugs** in the A1 and A2 versions of the
|
||||
chip to be acceptable. Here's what to look for: On the top line of the chip you'll see the designation A80386-16.
|
||||
If it says A80386-16ES, then it's an engineering sample and the vendor is a *cheapskate*. The samples have the revision
|
||||
number on the top line as A1, A2, or B1. Look no further.
|
||||
|
||||
> For the rest of you, look at the second line on the chip. If it's S40344 then you have a B1 chip. S40334 is the
|
||||
A2 revision and S40276 is the A1 revision....
|
||||
|
||||
---
|
||||
|
||||
Excerpt from "Tutor", [PC Magazine, October 15, 1991](https://books.google.com/books?id=tSLe3yMjc-AC&pg=PT438&hl=en&sa=X&ved=0ahUKEwjR9MH4gOHJAhVT0mMKHc4tD0YQ6AEIKjAA#v=onepage),
|
||||
by Jeff Prosise:
|
||||
|
||||
> You can tell if you have a B0 or B1 Step level 386 by looking at the markings on the chip. If it has the ID number
|
||||
S40336 or S40337 stamped on it, then it's a Step B0; if it's marked with S40343, S40344, or S40362, it's a Step B1.
|
||||
Some B0 and B1 chips were marked B0 or B1 rather than with an ID number.
|
||||
|
||||
---
|
||||
|
||||
Excerpt from "[CPU Identification by the Windows Kernel](http://www.geoffchappell.com/studies/windows/km/cpu/index.htm)", by Geoff Chappell:
|
||||
|
||||
> Finer identification of 80386 processors is largely academic. Whatever the model or stepping, the 80386 processor
|
||||
is unsupported since [Windows NT] version 4.0, and soon causes the bug check UNSUPPORTED_PROCESSOR (0x5D), though not
|
||||
without the kernel having worked its way through more tests for defects to identify models and steppings. For any 80386
|
||||
processor that passes all tests, the model and stepping leap ahead to 3 and 1. Version 3.51, which was the last to
|
||||
support the 80386 (and only then in a single-processor configuration), rejects any 80386 that does not pass all these
|
||||
tests.
|
||||
|
||||
Family Model Stepping Test
|
||||
------ ----- -------- ----
|
||||
3 0 0 32-bit MUL not reliably correct
|
||||
3 1 0 supports XBTS instruction
|
||||
3 1 1 set TF bit (0x0100) in EFLAGS causes Debug exception (interrupt 0x01) only at completion of REP MOVSB
|
||||
3 3 1
|
||||
|
||||
> The particular multiplication that distinguishes model 0 is of 0x81 by 0x0417A000. This same test was used by Microsoft
|
||||
at least as far back as Windows 3.10 Enhanced Mode, to advise:
|
||||
|
||||
The Intel 80386 processor in this computer does not reliably execute 32-bit
|
||||
multiply operations. Windows usually works correctly on computers with this
|
||||
problem but may occasionally fail. You may want to replace your 80386 processor.
|
||||
Press any key to continue...
|
||||
|
||||
> The instruction whose support is tested for model 1 stepping 0 has opcode bytes 0x0F 0xA6 followed by a Mod R/M byte
|
||||
and by whatever more this byte indicates is needed for the operand. This opcode is disassembled as XBTS by Microsoft’s
|
||||
DUMPBIN utility from Visual C++, and has been since at least the mid-90s. However, the same opcode was apparently reused
|
||||
for the CMPXCHG instruction on some 80486 processors. The confusion seems to have left a lasting mark: Intel’s opcode
|
||||
charts leave 0x0F 0xA6 unassigned even now. The specific test performed by the Windows kernel is to load EAX and EDX
|
||||
with zero and ECX with 0xFF00. If executing XBTS ECX,EDX does not cause an Invalid Opcode exception and clears ecx to
|
||||
zero (which CMPXCHG ECX,EDX would not), then XBTS is deemed to be supported and the processor is model 1 stepping 0.
|
||||
This case of 80386 processor also was known to Windows 3.10 Enhanced Mode, and was rejected as fatal:
|
||||
|
||||
Windows may not run correctly with the 80386 processor in this computer.
|
||||
|
||||
Upgrade your 80386 processor or start Windows in standard mode by typing
|
||||
WIN /s at the MS-DOS prompt.
|
||||
|
||||
> When string instructions such as MOVSB are repeated because of a REP prefix, each operation is ordinarily interruptible.
|
||||
As Intel says (for REP in the [Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference N-Z](http://www.intel.com/design/processor/manuals/253667.pdf)),
|
||||
this “allows long string operations to proceed without affecting the interrupt response time of the system.” It ordinarily
|
||||
applies also to the Debug exception, such as raised by the processor at the end of executing an instruction for which the TF
|
||||
bit is set in the EFLAGS when the instruction started. Programmers may have noticed this in the real world of assembly-language
|
||||
debugging. If the debugger actually does implement its trace command as a trace, as opposed to setting an INT 3 breakpoint
|
||||
where the instruction is calculated to end, then a two-byte REP MOVSB may take many keystrokes to trace through! That
|
||||
model 1 stepping 1 traces through a REP MOVSB without interruption may be helpful when debugging, but it is surely a defect.
|
||||
|
||||
---
|
||||
|
||||
More examples of problems with early 80386 CPUs are posted in "[The Old New Thing](http://blogs.msdn.com/b/oldnewthing/)"
|
||||
blog. Here are some highlights from "[My, what strange NOPs you have!](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx)",
|
||||
by Raymond Chen:
|
||||
|
||||
> [I]f the instruction following a string operation (such as movs) uses opposite-sized addresses from that in the string
|
||||
instruction (for example, if you performed a movs es:[edi], ds:[esi] followed by a mov ax, [bx]) or if the following
|
||||
instruction accessed an opposite-sized stack (for example, if you performed a movs es:[edi], ds:[esi] on a 16-bit stack,
|
||||
and the next instruction was a push), then the movs instruction would not operate correctly....
|
||||
|
||||
> [T]here was one bug that manifested itself in incorrect instruction decoding if a conditional branch instruction
|
||||
had just the right sequence of taken/not-taken history, and the branch instruction was followed immediately by a selector load,
|
||||
and one of the first two instructions at the destination of the branch was itself a jump, call, or return. The easy workaround:
|
||||
Insert a NOP between the branch and the selector load....
|
||||
|
||||
> [T]he B1 stepping did not support virtual memory in the first 64KB of memory. Fine, don't use virtual memory there....
|
||||
|
||||
> If virtual memory was enabled, if a certain race condition was encountered inside the hardware prefetch, and if you executed
|
||||
a floating point coprocessor instruction that accessed memory at an address in the range 0x800000F8 through 0x800000FF,
|
||||
then the CPU would end up reading from addresses 0x000000F8 through 0x0000000FF instead. This one was easy to work around:
|
||||
Never allocate valid memory at 0x80000xxx.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*February 23, 2015*
|
||||
210
_posts/2015-03-26-javascript-idiosyncrasies.md
Normal file
210
_posts/2015-03-26-javascript-idiosyncrasies.md
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
layout: post
|
||||
title: JavaScript Idiosyncrasies
|
||||
date: 2015-03-26 11:00:00
|
||||
category: JavaScript
|
||||
permalink: /blog/2015/03/26/
|
||||
---
|
||||
|
||||
Time to mention a few JavaScript idiosyncrasies, and how I deal with them.
|
||||
|
||||
Also, see my previous posts on [PCjs Coding Conventions](/blog/2014/09/30/) and [JavaScript Negativity](/blog/2014/10/26/).
|
||||
|
||||
### Strict Equality
|
||||
|
||||
Many JavaScript websites will advise you to *never* use the "==" and "!=" JavaScript operators, because when they compare
|
||||
variables containing different data types, JavaScript will coerce one of the operands to a matching type, sometimes in
|
||||
unexpected ways. We can thank the early days of JavaScript for this feature, when it was trying to be extraordinarily
|
||||
forgiving of sloppy code. I'm not going to list all the odd results that can arise from JavaScript's operand coercion,
|
||||
because there are more than enough examples on the web already.
|
||||
|
||||
To avoid unexpected type coercion, and thus unexpected matches and/or mismatches, the usual advice is to *always* use
|
||||
strict equality operators ("===" and "!==").
|
||||
|
||||
I disagree.
|
||||
|
||||
In well-written code, the variable data types should always be clear. In fact, the more you're able to
|
||||
use [JSDoc](http://developers.google.com/closure/compiler/docs/js-for-compiler) types to declare the data types
|
||||
of all your parameters, return values, and other variables, the fewer errors you'll have. As long as you're always
|
||||
comparing variables with matching types, there shouldn't be any unexpected coercions.
|
||||
|
||||
Obviously, there will be times when a polymorphic variable is required, especially when dealing with APIs that can
|
||||
return multiple types. But those should be the exception, not the rule.
|
||||
|
||||
Another exception is optional parameters. When I write a method with optional parameters, I generally allow those
|
||||
parameters to either be omitted (ie, *undefined*) or set to *null*. Using "==", you can check for either value with
|
||||
a single comparison:
|
||||
|
||||
``` javascript
|
||||
if (parameter == null) { ... }
|
||||
```
|
||||
|
||||
whereas strict equality requires more work:
|
||||
|
||||
``` javascript
|
||||
if (parameter === undefined || parameter === null) { ... }
|
||||
```
|
||||
|
||||
This is one of those times when coercion (of *undefined* to *null*), and the use of "non-strict" operators, is beneficial.
|
||||
Here's another:
|
||||
|
||||
``` javascript
|
||||
if (!b) { ... }
|
||||
```
|
||||
|
||||
Coercing a value to *boolean* is a popular way of checking for all "falsy" values (ie, *undefined*, *null*,
|
||||
0, false, "", NaN, etc). It is shorthand for:
|
||||
|
||||
``` javascript
|
||||
if (b == false) { ... }
|
||||
```
|
||||
|
||||
yet I suspect the proponents of strict equality would embrace the former while rejecting the latter.
|
||||
|
||||
However, I don't recommend "falsy" checks for optional parameters:
|
||||
|
||||
``` javascript
|
||||
if (!parameter) { ... }
|
||||
```
|
||||
|
||||
because often a valid numeric parameter might include 0, or a valid string parameter might include "", so it's better
|
||||
to do this:
|
||||
|
||||
``` javascript
|
||||
if (parameter == null) { ... }
|
||||
```
|
||||
|
||||
and obviously if *null* is also a acceptable value, then you should definitely use strict equality:
|
||||
|
||||
``` javascript
|
||||
if (parameter === undefined) { ... }
|
||||
```
|
||||
|
||||
Problems with type coercion are **NOT** problems caused by a poor choice of operators, so trying to make
|
||||
those problems go away by artificially limiting your choice of operators seems like the wrong solution.
|
||||
Type coercion problems are, by definition, problems involving mismatched types. Solutions include:
|
||||
|
||||
- Avoid comparing variables of different types; or
|
||||
- Convert your variables to matching types first; or
|
||||
- Use strict equality operators (just don't use them mindlessly)
|
||||
|
||||
Explicitly convert variables to a single type whenever possible. For example, I might define a method
|
||||
that accepts an optional numeric parameter, with a documented default value when it's omitted. I think it's
|
||||
important make that parameter unambiguously numeric as soon as possible; eg:
|
||||
|
||||
``` javascript
|
||||
/**
|
||||
* foo(n)
|
||||
*
|
||||
* Performs a mathematical operation on n and returns a result.
|
||||
*
|
||||
* @param {number} [n] is an optional parameter (defaults to zero if omitted)
|
||||
* @return {number}
|
||||
*/
|
||||
function foo(n) {
|
||||
n = n || 0;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The expression `n || 0` might seem pointless, because *undefined* and *zero* are equivalent in a "falsy" sense, but
|
||||
*undefined* is not a number, and there will be fewer problems downstream if you ensure that n is *always* a number.
|
||||
|
||||
### Enumerating Array or Object Properties
|
||||
|
||||
When using *for*...*in* loops like this:
|
||||
|
||||
``` javascript
|
||||
var a = [100, 200, 300];
|
||||
for (var i in a) { ... }
|
||||
```
|
||||
|
||||
the type of variable *i* will be **string** rather than **number**; that is, it will contain "0", "1", and "2" rather
|
||||
than 0, 1, and 2. If you then use *i* to set a matching element in another array, that element will not be stored in
|
||||
the same (numeric) position as the original array.
|
||||
|
||||
One solution is to convert *i* to a **number**:
|
||||
|
||||
``` javascript
|
||||
parseInt(i, 10);
|
||||
```
|
||||
|
||||
However, a more elegant solution is to use the unary "+" operator to coerce the **string** to a **number**:
|
||||
|
||||
``` javascript
|
||||
+i;
|
||||
```
|
||||
|
||||
The same problem arises with objects using numeric properties. And watch out for JavaScript's automatic base
|
||||
conversion of numeric properties. For example, when you enumerate the properties of object "o":
|
||||
|
||||
``` javascript
|
||||
var o = {
|
||||
0x20: ' ',
|
||||
0x41: 'A'
|
||||
};
|
||||
```
|
||||
|
||||
you will get the strings "32" and "65", not "0x20" and "0x41". You must quote your property names to prevent
|
||||
any conversion; eg:
|
||||
|
||||
``` javascript
|
||||
var o = {
|
||||
"0x20": ' ',
|
||||
"0x41": 'A'
|
||||
};
|
||||
```
|
||||
|
||||
Numeric properties can always be safely converted using the unary "+" operator, regardless whether they were quoted
|
||||
or not.
|
||||
|
||||
The unary "+" is a great alternative to parseInt(), but be mindful of their differences. One important difference
|
||||
is that parseInt() will stop when it encounters an invalid digit, returning whatever value was parsed up to that point,
|
||||
whereas unary "+" conversion will return *NaN* if there are any invalid digits in the string.
|
||||
|
||||
### Shift Counts For Bitwise Shifts
|
||||
|
||||
It turns out that shifting an integer value by more than 31 bits in either direction may not shift as many bits as
|
||||
you'd expect. For example:
|
||||
|
||||
``` javascript
|
||||
n = 0x10000000;
|
||||
n >>>= 33;
|
||||
```
|
||||
|
||||
will shift n by only *one* bit, not 33 bits, and the result will be 0x08000000, not zero. This is because,
|
||||
just like the shift instructions on 32-bit Intel processors, JavaScript converts the shift count to a *mod 32* value
|
||||
(in other words, it truncates the shift count to a 5-bit value).
|
||||
|
||||
So the above example is equivalent to:
|
||||
|
||||
``` javascript
|
||||
n >>>= 1;
|
||||
```
|
||||
|
||||
If you really need larger shift counts to work in a consistent manner, you can perform multiple shifts, where each
|
||||
shift count is in the range 0-31. Here's one way to shift a number 33 bits:
|
||||
|
||||
``` javascript
|
||||
n = (n >>> 31) >>> 2;
|
||||
```
|
||||
|
||||
Also, it's not quite correct to say that a shift count of zero has *no* effect on a number:
|
||||
|
||||
``` javascript
|
||||
n = 0x88888888|0; // n is displayed as -2004318072
|
||||
n >>>= 0; // n is displayed as 2290649224
|
||||
```
|
||||
|
||||
It's true that the bottom 32 bits of the number were not changed, but a side-effect of the unsigned shift operator
|
||||
is that all the upper sign bits are stripped from the (64-bit) result.
|
||||
|
||||
Similarly, as soon as you perform any other bitwise operation on the number, even one that does not modify the low
|
||||
32 bits, the upper bits will revert to the sign of the lower 32-bit value:
|
||||
|
||||
``` javascript
|
||||
n |= 0; // n is displayed as -2004318072 again
|
||||
```
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*March 26, 2015*
|
||||
154
_posts/2015-04-16-compaq-deskpro-386-update.md
Normal file
154
_posts/2015-04-16-compaq-deskpro-386-update.md
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
---
|
||||
layout: post
|
||||
title: COMPAQ DeskPro 386 Update
|
||||
date: 2015-04-16 11:00:00
|
||||
category: 80386
|
||||
permalink: /blog/2015/04/16/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: deskpro386
|
||||
debugger: true
|
||||
uncompiled: true
|
||||
config: /devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/machine.xml
|
||||
---
|
||||
|
||||
PCx86 can now boot the [COMPAQ DeskPro 386/16 ROM BIOS](/devices/pcx86/rom/compaq/deskpro386/).
|
||||
|
||||
There's still a problem with the Hard Drive Controller, which I haven't looked into yet,
|
||||
but booting from a floppy works.
|
||||
|
||||
While working through issues with this ROM BIOS, I created some lightly-annotated
|
||||
[source code](/devices/pcx86/rom/compaq/deskpro386/1988-01-28/1988-01-28.asm) that can be re-assembled
|
||||
with [NASM](http://www.nasm.us/). The initial process of creating the source code is
|
||||
explained [here](/devices/pcx86/rom/compaq/deskpro386/#recreating-rom-source-code).
|
||||
|
||||
At the top of the source code, I explain a few important details about ROM addresses that
|
||||
are worth recapping here:
|
||||
|
||||
> This 32Kb ROM image is ORG'ed at 0x8000, because most of its code is designed to run
|
||||
at real-mode addresses F000:8000 through F000:FFFF.
|
||||
|
||||
> And even though the 80386 resets with CS:IP set to F000:FFF0, the physical base address
|
||||
of CS is set to %FFFF0000, which means the ROM must also be mapped at physical addresses
|
||||
%FFFF8000 through %FFFFFFFF.
|
||||
|
||||
> Additionally, DeskPro 386 systems mirror this 32Kb ROM at real-mode address F000:0000
|
||||
through F000:7FFF. Once again, that region is mirrored at physical addresses %FFFF0000
|
||||
through %FFFF7FFFF.
|
||||
|
||||
> In other words, both 32Kb halves of the last 64Kb of both the first and last megabyte
|
||||
of the 80386's 4Gb address space are physically mapped to this ROM image.
|
||||
|
||||
> Finally, the DeskPro 386 has a "RAM Relocation" feature that allows 128Kb of RAM at
|
||||
%00FE0000 through %00FFFFFF to be mapped to %000E0000 through %000FFFFF, effectively
|
||||
replacing the ROM in the first megabyte with write-protected RAM; the top 64Kb of that
|
||||
RAM must first be initialized with the 64Kb at %000F0000 prior to remapping. It's also
|
||||
possible to copy external ROMs from %000C0000 through %000EFFFF into the bottom 64Kb of
|
||||
that RAM, but this is only done for ROMs known to contain relocatable code; eg, a COMPAQ
|
||||
Video Graphics Controller (VGC) Board.
|
||||
|
||||
> Every DeskPro 386 system must have a MINIMUM of 1Mb of RAM, of which either 256Kb,
|
||||
512Kb, or 640Kb can be physically mapped as conventional memory (at the bottom of the
|
||||
first megabyte), with the remainder (either 768Kb, 512Kb, or 384Kb) physically mapped
|
||||
to the top of the 16th megabyte (ending at address %00FFFFFF), the last 128Kb of which
|
||||
is used by the "RAM Relocation" feature. The remaining memory immediately below that
|
||||
128Kb (ie, below %00FE0000) can only be accessed by special system software, such as CEMM.
|
||||
|
||||
> COMPAQ refers to that remaining memory as "Compaq Built-in Memory".
|
||||
|
||||
So there you have it. Once the ROM has relocated itself to RAM at the top of the 16th
|
||||
megabyte, there are no less than THREE physical address ranges where ROM code and data
|
||||
structures can be accessed:
|
||||
|
||||
1. %000F0000 through %000FFFFF (aka real-mode adresses F000:0000 through F000:FFFF)
|
||||
2. %00FF0000 through %00FFFFFF (the relocated copy)
|
||||
3. %FFFF0000 through %FFFFFFFF (the physical alias of %000F0000 through %000FFFFF)
|
||||
|
||||
As you would expect, most of the ROM's code and data references are to first megabyte,
|
||||
since most of the code is designed to run in real-mode. But there are portions that
|
||||
run in protected-mode, and those portions are much less consistent about which address
|
||||
range to use -- no doubt, in part, because it makes no difference. The ROM does
|
||||
not run with paging enabled, so any physical address is as easy to access as any other.
|
||||
|
||||
Unless, of course, the A20 line is disabled. In that case, only the first range is
|
||||
accessible; the other two are not.
|
||||
|
||||
April 19, 2015 Update
|
||||
---
|
||||
Thanks to some sleuthing by [Michal Necasek](http://os2museum.com/), it turns out that my
|
||||
assumptions about A20 management on the COMPAQ DeskPro 386 were incorrect.
|
||||
|
||||
He noted that, on page 398 of "DOS Internals" by Geoff Chappell, (c) 1994, the author says:
|
||||
|
||||
> On a machine that controls the A20 by passing the address line through an AND gate with a
|
||||
signal from some bit at an I/O port, the A20MAP program should produce a map similar to:
|
||||
|
||||
Memory mapping with disabled A20 line:
|
||||
|
||||
0MB -> 0MB
|
||||
1MB -> 0MB
|
||||
2MB -> 2MB
|
||||
3MB -> 2MB
|
||||
4MB -> 4MB
|
||||
5MB -> 4MB
|
||||
6MB -> 6MB
|
||||
7MB -> 6MB
|
||||
|
||||
> showing wrap-around for every second megabyte. It is also possible to include other address lines
|
||||
in the controlling mechanism, which may reduce the incidence of wrap-around, as with a Compaq
|
||||
DeskPro:
|
||||
|
||||
0MB -> 0MB
|
||||
1MB -> 0MB
|
||||
2MB -> 2MB
|
||||
3MB -> 3MB
|
||||
4MB -> 4MB
|
||||
|
||||
---
|
||||
|
||||
This means that the DeskPro ROM BIOS can, in fact, access its own code and data at ANY of the above
|
||||
three physical address ranges at any time, regardless whether A20 is disabled or not.
|
||||
|
||||
Which is a good thing, because I came across at least one code sequence in the ROM BIOS that enters
|
||||
protected-mode with A20 disabled -- an unwise thing to do on most machines:
|
||||
|
||||
;
|
||||
; When we arrive here, the A20 line has been disabled; on most systems, that would
|
||||
; mean that the ROM's GDT would only be accessible at the "low" ROM address (%0F0730),
|
||||
; not the "high" address (%FF0730). But fortunately, A20 management on COMPAQ
|
||||
; DeskPros affects wrap-around only from the 1st to the 2nd megabyte; no other address
|
||||
; range is affected.
|
||||
;
|
||||
; FYI, it seems this code doesn't do anything if bits 6 and 7 of the RAM Settings
|
||||
; register are set to anything other than 0x40 (ie, it returns to real-mode almost
|
||||
; immediately after entering protected-mode).
|
||||
;
|
||||
lgdt [cs:0x077e] ; 0000F498 2E0F01167E07; load [gdtr_hi] into GDTR
|
||||
mov eax,cr0 ; 0000F49E 0F2000
|
||||
or ax,0x1 ; 0000F4A1 0D0100
|
||||
mov cr0,eax ; 0000F4A4 0F2200
|
||||
jmp 0x28:xf4ac ; 0000F4A7 EAACF42800
|
||||
|
||||
Before fully understanding the DeskPro's unusual A20 management, PCx86 worked around it by
|
||||
redirecting all A20 changes from the Bus component to the CPU component, giving the CPU first
|
||||
crack at any changes to A20. If the CPU was in real-mode, it would simply pass the A20 request
|
||||
on to the Bus. However, if the CPU was in protected-mode, it would maintain the requested
|
||||
"logical" A20 state but ensure that the "physical" state of A20 was always enabled. In short,
|
||||
it was no longer possible for the CPU to be in protected-mode AND for the A20 line to be disabled;
|
||||
when one was enabled, the other was enabled as well.
|
||||
|
||||
I'm in the process of replacing that work-around with a much more compatible change, at least
|
||||
on 32-bit bus configurations, which involves changing the physical address map for the 2nd megabyte
|
||||
to match that of the 1st megabyte whenever A20 is disabled. I could probably get away with remapping
|
||||
only the first 64Kb of the 2nd megabyte, but until I'm actually able to run some tests on a real
|
||||
DeskPro 386, I'm going to assume COMPAQ's A20 implementation affected the entire 2nd megabyte.
|
||||
|
||||
Here's my [COMPAQ DeskPro 386/16](/devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/) test
|
||||
configuration. Set a breakpoint at F000:F498 ("bp f000:f498") in the Debugger panel to see the above
|
||||
code in action. When the machine is operating in real-mode, you can use the "rp" command to dump all
|
||||
the registers, including the current base and limit values loaded into the segment registers.
|
||||
|
||||
{% include machine.html id="deskpro386" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*April 16-19, 2015*
|
||||
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://archive.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*
|
||||
142
_posts/2015-06-01-debugging-the-ibm-vga-rom.md
Normal file
142
_posts/2015-06-01-debugging-the-ibm-vga-rom.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
---
|
||||
layout: post
|
||||
title: Debugging the IBM VGA ROM
|
||||
date: 2015-06-01 11:00:00
|
||||
category: Video
|
||||
permalink: /blog/2015/06/01/
|
||||
---
|
||||
|
||||
The IBM VGA ("Video Graphics Array") standard was introduced as part of the IBM PS/2 line of computers;
|
||||
it was not a feature you could purchase or install in older PC, XT or AT-compatible machines. In fact, full VGA
|
||||
support was not even available in all PS/2 models.
|
||||
|
||||
Of the first four PS/2 models -- the 8086-based Model 30, the 80286-based Model 50 and Model 60, and the 80386-based
|
||||
Model 80 -- VGA support was available only in the three higher-end models. The Model 30 came with MCGA ("Multicolor
|
||||
Graphics Array") video hardware that supported a subset of VGA modes (eg, 640x480 2-color and 320x200 256-color graphics).
|
||||
|
||||
It wasn't until October 1987 that IBM finally introduced an 8-bit ISA card that brought VGA capability to older PCs.
|
||||
The card was called the **IBM PS/2 Display Adapter**. However, I think the name is a bit confusing, since the card
|
||||
could only be used in PC, XT, and AT-compatible systems. I'll refer to it here simply as the IBM VGA.
|
||||
|
||||
The VGA ROM used here is assumed to have come from an original IBM VGA. It's unknown if IBM ever made any
|
||||
revisions to the VGA ROM. With the introduction of the PS/2 family and the VGA, IBM decided to no longer publish
|
||||
the source code for its ROMs, so I've created some assemblable source code from the IBM VGA ROM
|
||||
[here](/devices/pcx86/video/ibm/vga/).
|
||||
|
||||
I've finally started debugging a machine configuration that uses the IBM VGA ROM. Since the VGA and the 80386 are
|
||||
contemporaries, I'm using an [80386 machine configuration](/devices/pcx86/machine/compaq/deskpro386/vga/2048kb/debugger/).
|
||||
However, I don't expect the IBM VGA ROM to require any 80386 support or PS/2-specific features.
|
||||
|
||||
The first problem I ran into was here:
|
||||
|
||||
;
|
||||
; Initialize the ROM BIOS Video Mode Options byte @40:0087 (default to color and 256Kb of RAM)
|
||||
;
|
||||
mov byte [0x487],0x60 ; 0000008D
|
||||
;
|
||||
; The x100 subroutine alternately enables port 0x3B? and 0x3D? decoding, verifying that there is
|
||||
; no response on opposing ports 0x3D? and 0x3B?, respectively; otherwise, it assumes that another
|
||||
; video card must exist and attempts to select co-existing settings for the VGA. For example, if
|
||||
; there is an unexpected response on the color ports, the VGA ROM will default to mono operation.
|
||||
;
|
||||
call x100 ; 00000092
|
||||
|
||||
The Video component installs I/O port handlers for all possible I/O ranges; when a range isn't being used, the
|
||||
associated I/O operations are redirected to a dummy Card, so that the active Card isn't affected. Here,
|
||||
however, that was insufficient. If the VGA is the only installed video card, the VGA ROM expects *NO RESPONSE*
|
||||
on inactive CRTC ports. So I've changed the CRTC I/O handlers to check the Card's fActive flag. This seems
|
||||
like a safe and logical change, but I still have to check for backward-compatibility issues with older ROMs.
|
||||
|
||||
Other problems included:
|
||||
|
||||
* Some bugs in Read Mode 1 that caused a memory test failure
|
||||
* Horizontal and vertical retrace timing issues (the ROM requires a specific number of intervals per second)
|
||||
* Differences between the EGA and VGA in the SWSENSE bit (bit 4) of Input Status Register 0
|
||||
|
||||
The last problem was the most puzzling, because the ROM programs a series of values into the first DAC register,
|
||||
and expects the SWSENSE bit of Input Status Register 0 to change in very specific ways, depending on the kind
|
||||
of monitor attached.
|
||||
|
||||
I've not found any hardware documentation that explains exactly how this should work. IBM's own Technical Reference
|
||||
material is extremely vague:
|
||||
|
||||
"Bit 4: Switch Sense Bit - This bit allows the system microprocessor to read the switch sense line.
|
||||
This bit allows the power-on self-test to determine if a monochrome or color display is connected to
|
||||
the system."
|
||||
|
||||
I've hard-coded a solution that assumes a color monitor. Support for using a monochrome monitor with an EGA was
|
||||
never completed, and this is another related issue that will have to be resolved for the VGA as well.
|
||||
|
||||
---
|
||||
|
||||
While debugging and fixing assorted IBM VGA issues, I made a table of all the register values for the video
|
||||
modes commonly used on the IBM VGA. Here's that table:
|
||||
|
||||
INT 0x10 Mode Requested: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x0D 0x0E 0x10 0x12 0x13
|
||||
|
||||
BIOSMODE: 0x01 0x01 0x03 0x03 0x04 0x04 0x06 0x0D 0x0E 0x10 0x12 0x13
|
||||
CRTC[0x00]: HTOTAL 0x2D 0x2D 0x5F 0x5F 0x2D 0x2D 0x5F 0x2D 0x5F 0x5F 0x5F 0x5F
|
||||
CRTC[0x01]: HDISP_END 0x27 0x27 0x4F 0x4F 0x27 0x27 0x4F 0x27 0x4F 0x4F 0x4F 0x4F
|
||||
CRTC[0x02]: HBLANK_START 0x28 0x28 0x50 0x50 0x28 0x28 0x50 0x28 0x50 0x50 0x50 0x50
|
||||
CRTC[0x03]: HBLANK_END 0x90 0x90 0x82 0x82 0x90 0x90 0x82 0x90 0x82 0x82 0x82 0x82
|
||||
CRTC[0x04]: HRETRACE_START 0x2B 0x2B 0x55 0x55 0x2B 0x2B 0x54 0x2B 0x54 0x54 0x54 0x54
|
||||
CRTC[0x05]: HRETRACE_END 0xA0 0xA0 0x81 0x81 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80
|
||||
CRTC[0x06]: VTOTAL 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0x0B 0xBF
|
||||
CRTC[0x07]: OVERFLOW 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x3E 0x1F
|
||||
CRTC[0x08]: PRESET_ROW 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x09]: MAX_SCAN 0x4F 0x4F 0x4F 0x4F 0xC1 0xC1 0xC1 0xC0 0xC0 0x40 0x40 0x41
|
||||
CRTC[0x0A]: CURSOR_START 0x0D 0x0D 0x0D 0x0D 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0B]: CURSOR_END 0x0E 0x0E 0x0E 0x0E 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0C]: START_ADDR_HI 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0D]: START_ADDR_LO 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
CRTC[0x0E]: CURSOR_ADDR_HI 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00
|
||||
CRTC[0x0F]: CURSOR_ADDR_LO 0x19 0x19 0x41 0x41 0x19 0x19 0x41 0x19 0x41 0x41 0xE1 0xA2
|
||||
CRTC[0x10]: VRETRACE_START 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x83 0xEA 0x9C
|
||||
CRTC[0x11]: VRETRACE_END 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x85 0x8C 0x8E
|
||||
CRTC[0x12]: VDISP_END 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x5D 0xDF 0x8F
|
||||
CRTC[0x13]: OFFSET 0x14 0x14 0x28 0x28 0x14 0x14 0x28 0x14 0x28 0x28 0x28 0x28
|
||||
CRTC[0x14]: UNDERLINE 0x1F 0x1F 0x1F 0x1F 0x00 0x00 0x00 0x00 0x00 0x0F 0x00 0x40
|
||||
CRTC[0x15]: VBLANK_START 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x63 0xE7 0x96
|
||||
CRTC[0x16]: VBLANK_END 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xBA 0x04 0xB9
|
||||
CRTC[0x17]: MODE_CTRL 0xA3 0xA3 0xA3 0xA3 0xA2 0xA2 0xC2 0xE3 0xE3 0xE3 0xE3 0xA3
|
||||
CRTC[0x18]: LINE_COMPARE 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
||||
GRC[0x00]: SRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x01]: ESRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x02]: COLORCMP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x03]: DATAROT 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x04]: READMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
GRC[0x05]: MODE 0x10 0x10 0x10 0x10 0x30 0x30 0x00 0x00 0x00 0x00 0x00 0x40
|
||||
GRC[0x06]: MISC 0x0E 0x0E 0x0E 0x0E 0x0F 0x0F 0x0D 0x05 0x05 0x05 0x05 0x05
|
||||
GRC[0x07]: COLORDC 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0F 0x0F 0x0F 0x0F 0x0F
|
||||
GRC[0x08]: BITMASK 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
||||
SEQ[0x00]: RESET 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03
|
||||
SEQ[0x01]: CLOCKING 0x08 0x08 0x00 0x00 0x09 0x09 0x01 0x09 0x01 0x01 0x01 0x01
|
||||
SEQ[0x02]: MAPMASK 0x03 0x03 0x03 0x03 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
|
||||
SEQ[0x03]: CHARMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
SEQ[0x04]: MEMMODE 0x03 0x03 0x03 0x03 0x02 0x02 0x06 0x06 0x06 0x06 0x06 0x0E
|
||||
ATC[0x00]: PAL00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
ATC[0x01]: PAL01 0x01 0x01 0x01 0x01 0x13 0x13 0x17 0x01 0x01 0x01 0x01 0x01
|
||||
ATC[0x02]: PAL02 0x02 0x02 0x02 0x02 0x15 0x15 0x17 0x02 0x02 0x02 0x02 0x02
|
||||
ATC[0x03]: PAL03 0x03 0x03 0x03 0x03 0x17 0x17 0x17 0x03 0x03 0x03 0x03 0x03
|
||||
ATC[0x04]: PAL04 0x04 0x04 0x04 0x04 0x02 0x02 0x17 0x04 0x04 0x04 0x04 0x04
|
||||
ATC[0x05]: PAL05 0x05 0x05 0x05 0x05 0x04 0x04 0x17 0x05 0x05 0x05 0x05 0x05
|
||||
ATC[0x06]: PAL06 0x14 0x14 0x14 0x14 0x06 0x06 0x17 0x06 0x06 0x14 0x14 0x06
|
||||
ATC[0x07]: PAL07 0x07 0x07 0x07 0x07 0x07 0x07 0x17 0x07 0x07 0x07 0x07 0x07
|
||||
ATC[0x08]: PAL08 0x38 0x38 0x38 0x38 0x10 0x10 0x17 0x10 0x10 0x38 0x38 0x08
|
||||
ATC[0x09]: PAL09 0x39 0x39 0x39 0x39 0x11 0x11 0x17 0x11 0x11 0x39 0x39 0x09
|
||||
ATC[0x0A]: PAL0A 0x3A 0x3A 0x3A 0x3A 0x12 0x12 0x17 0x12 0x12 0x3A 0x3A 0x0A
|
||||
ATC[0x0B]: PAL0B 0x3B 0x3B 0x3B 0x3B 0x13 0x13 0x17 0x13 0x13 0x3B 0x3B 0x0B
|
||||
ATC[0x0C]: PAL0C 0x3C 0x3C 0x3C 0x3C 0x14 0x14 0x17 0x14 0x14 0x3C 0x3C 0x0C
|
||||
ATC[0x0D]: PAL0D 0x3D 0x3D 0x3D 0x3D 0x15 0x15 0x17 0x15 0x15 0x3D 0x3D 0x0D
|
||||
ATC[0x0E]: PAL0E 0x3E 0x3E 0x3E 0x3E 0x16 0x16 0x17 0x16 0x16 0x3E 0x3E 0x0E
|
||||
ATC[0x0F]: PAL0F 0x3F 0x3F 0x3F 0x3F 0x17 0x17 0x17 0x17 0x17 0x3F 0x3F 0x0F
|
||||
ATC[0x10]: MODE 0x0C 0x0C 0x0C 0x0C 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x41
|
||||
ATC[0x11]: OVERSCAN 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
ATC[0x12]: PLANES 0x0F 0x0F 0x0F 0x0F 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
|
||||
ATC[0x13]: HPAN 0x08 0x08 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
|
||||
In addition, I've created a [VGA Tests](/tests/pc/vga/) directory to hold VGA test and sample code that PCjs can
|
||||
now successfully run (for the most part). See that directory for more details.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*June 1, 2015 (Updated July 9, 2015)*
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
---
|
||||
layout: post
|
||||
title: The Strange Case of the EGA Graphics Scroll Bug
|
||||
date: 2015-06-05 11:00:00
|
||||
category: Video
|
||||
permalink: /blog/2015/06/05/
|
||||
---
|
||||
|
||||
I was playing with different video modes using this [IBM PC AT w/EGA](/devices/pcx86/machine/5170/ega/640kb/rev1/debugger/),
|
||||
and I discovered an odd problem.
|
||||
|
||||
For example, when I ran this code:
|
||||
|
||||
A>b:debug
|
||||
-a
|
||||
0CE0:0100 mov ax,e
|
||||
0CE0:0103 int 10
|
||||
0CE0:0105 int 3
|
||||
0CE0:0106
|
||||
-g
|
||||
|
||||
the following "text" correctly appeared at the top of the screen, in 640x200 16-color graphics mode 0x0E:
|
||||
|
||||
AX=0B01 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
|
||||
DS=0CE0 ES=0CE0 SS=0CE0 CS=0CE0 IP=0105 NV UP EI PL NZ NA PO NC
|
||||
0CE0:0105 CC INT 3
|
||||
-
|
||||
|
||||
And when I typed "q", then "cls" and finally "dir", the screen filled with DOS directory contents.
|
||||
|
||||
But as soon as the screen started to scroll, the screen contents became garbled. Other EGA graphics modes,
|
||||
like 640x350 16-color mode 0x10, didn't have this problem.
|
||||
|
||||
To investigate, I set a breakpoint in the IBM EGA ROM where the scrolling starts, at 0xC000:12EA (see p.130 of the
|
||||
"IBM Enhanced Graphics Adapter" Technical Reference document):
|
||||
|
||||
CRANK_A:
|
||||
PUSH CX
|
||||
MOV CL,DL
|
||||
SUB CH,CH
|
||||
PUSH SI
|
||||
PUSH DI
|
||||
REP MOVSB
|
||||
POP DI
|
||||
POP SI
|
||||
ADD SI,BX
|
||||
ADD DI,BX
|
||||
POP CX
|
||||
LOOP CRANK_A
|
||||
|
||||
CX contains 0xC0 (192), which is the number of scan-lines to move up, and BX contains 0x50 (80), the number
|
||||
of bytes per scan-line.
|
||||
|
||||
When the breakpoint was hit, I dumped the video hardware state, using the Debugger's "*d video*" command.
|
||||
For comparison purposes, I've pasted the corresponding video state for mode 0x10 on the right-hand side.
|
||||
|
||||
breakpoint hit: C000:12EA (exec)
|
||||
stopped (175707689 ops, 800060264 cycles, 133470 ms, 5994308 hz)
|
||||
AX=020F BX=0050 CX=00C0 DX=1950 SP=0B58 BP=0511 SI=0280 DI=0000
|
||||
SS=011F DS=A000 ES=A000 PS=0246 V0 D0 I1 T0 S0 Z1 A0 P1 C0
|
||||
C000:12EA 51 PUSH CX
|
||||
|
||||
BIOSMODE: 0x0E BIOSMODE: 0x10
|
||||
CRTC[0x00]: HORZ_TOTAL 0x70 CRTC[0x00]: HORZ_TOTAL 0x5B
|
||||
CRTC[0x01]: HORZ_DISP_END 0x4F CRTC[0x01]: HORZ_DISP_END 0x4F
|
||||
CRTC[0x02]: HORZ_BLANK_START 0x59 CRTC[0x02]: HORZ_BLANK_START 0x53
|
||||
CRTC[0x03]: HORZ_BLANK_END 0x2D CRTC[0x03]: HORZ_BLANK_END 0x37
|
||||
CRTC[0x04]: HORZ_RETRACE_START 0x5E CRTC[0x04]: HORZ_RETRACE_START 0x52
|
||||
CRTC[0x05]: HORZ_RETRACE_END 0x06 CRTC[0x05]: HORZ_RETRACE_END 0x00
|
||||
CRTC[0x06]: VERT_TOTAL 0x04 CRTC[0x06]: VERT_TOTAL 0x6C
|
||||
CRTC[0x07]: OVERFLOW 0x11 CRTC[0x07]: OVERFLOW 0x1F
|
||||
CRTC[0x08]: PRESET_ROW_SCAN 0x00 CRTC[0x08]: PRESET_ROW_SCAN 0x00
|
||||
CRTC[0x09]: MAX_SCAN_LINE 0x00 CRTC[0x09]: MAX_SCAN_LINE 0x00
|
||||
CRTC[0x0A]: CURSOR_START 0x00 CRTC[0x0A]: CURSOR_START 0x00
|
||||
CRTC[0x0B]: CURSOR_END 0x01 CRTC[0x0B]: CURSOR_END 0x01
|
||||
CRTC[0x0C]: START_ADDR_HI 0x00 CRTC[0x0C]: START_ADDR_HI 0x00
|
||||
CRTC[0x0D]: START_ADDR_LO 0x00 CRTC[0x0D]: START_ADDR_LO 0x00
|
||||
CRTC[0x0E]: CURSOR_ADDR_HI 0x07 CRTC[0x0E]: CURSOR_ADDR_HI 0x01
|
||||
CRTC[0x0F]: CURSOR_ADDR_LO 0x80* CRTC[0x0F]: CURSOR_ADDR_LO 0x41*
|
||||
CRTC[0x10]: VERT_RETRACE_START 0xE0 CRTC[0x10]: VERT_RETRACE_START 0x5E
|
||||
CRTC[0x11]: VERT_RETRACE_END 0x23 CRTC[0x11]: VERT_RETRACE_END 0x2B
|
||||
CRTC[0x12]: VERT_DISP_END 0xC7 CRTC[0x12]: VERT_DISP_END 0x5D
|
||||
CRTC[0x13]: OFFSET 0x28 CRTC[0x13]: OFFSET 0x28
|
||||
CRTC[0x14]: UNDERLINE 0x00 CRTC[0x14]: UNDERLINE 0x0F
|
||||
CRTC[0x15]: VERT_BLANK_START 0xDF CRTC[0x15]: VERT_BLANK_START 0x5F
|
||||
CRTC[0x16]: VERT_BLANK_END 0xEF CRTC[0x16]: VERT_BLANK_END 0x0A
|
||||
CRTC[0x17]: MODE_CTRL 0xE3 CRTC[0x17]: MODE_CTRL 0xE3
|
||||
CRTC[0x18]: LINE_COMPARE 0xFF CRTC[0x18]: LINE_COMPARE 0xFF
|
||||
STATUS1: 0x01 STATUS1: 0x01
|
||||
ATCDATA: true ATCDATA: true
|
||||
ATC[0x00]: PAL00 0x00 ATC[0x00]: PAL00 0x00
|
||||
ATC[0x01]: PAL01 0x01 ATC[0x01]: PAL01 0x01
|
||||
ATC[0x02]: PAL02 0x02 ATC[0x02]: PAL02 0x02
|
||||
ATC[0x03]: PAL03 0x03 ATC[0x03]: PAL03 0x03
|
||||
ATC[0x04]: PAL04 0x04 ATC[0x04]: PAL04 0x04
|
||||
ATC[0x05]: PAL05 0x05 ATC[0x05]: PAL05 0x05
|
||||
ATC[0x06]: PAL06 0x06 ATC[0x06]: PAL06 0x14
|
||||
ATC[0x07]: PAL07 0x07 ATC[0x07]: PAL07 0x07
|
||||
ATC[0x08]: PAL08 0x10 ATC[0x08]: PAL08 0x38
|
||||
ATC[0x09]: PAL09 0x11 ATC[0x09]: PAL09 0x39
|
||||
ATC[0x0A]: PAL0A 0x12 ATC[0x0A]: PAL0A 0x3A
|
||||
ATC[0x0B]: PAL0B 0x13 ATC[0x0B]: PAL0B 0x3B
|
||||
ATC[0x0C]: PAL0C 0x14 ATC[0x0C]: PAL0C 0x3C
|
||||
ATC[0x0D]: PAL0D 0x15 ATC[0x0D]: PAL0D 0x3D
|
||||
ATC[0x0E]: PAL0E 0x16 ATC[0x0E]: PAL0E 0x3E
|
||||
ATC[0x0F]: PAL0F 0x17 ATC[0x0F]: PAL0F 0x3F
|
||||
ATC[0x10]: MODE 0x01 ATC[0x10]: MODE 0x01
|
||||
ATC[0x11]: OVERSCAN 0x00 ATC[0x11]: OVERSCAN 0x00
|
||||
ATC[0x12]: PLANES 0x0F ATC[0x12]: PLANES 0x0F
|
||||
ATC[0x13]: HORZPAN 0x00 ATC[0x13]: HORZPAN 0x00
|
||||
GRC[0x00]: SRESET 0x00 GRC[0x00]: SRESET 0x00
|
||||
GRC[0x01]: ESRESET 0x00 GRC[0x01]: ESRESET 0x00
|
||||
GRC[0x02]: COLORCMP 0x00 GRC[0x02]: COLORCMP 0x00
|
||||
GRC[0x03]: DATAROT 0x00 GRC[0x03]: DATAROT 0x00*
|
||||
GRC[0x04]: READMAP 0x00 GRC[0x04]: READMAP 0x00
|
||||
GRC[0x05]: MODE 0x11* GRC[0x05]: MODE 0x00
|
||||
GRC[0x06]: MISC 0x05 GRC[0x06]: MISC 0x05
|
||||
GRC[0x07]: COLORDC 0x0F GRC[0x07]: COLORDC 0x0F
|
||||
GRC[0x08]: BITMASK 0xFF GRC[0x08]: BITMASK 0xFF
|
||||
SEQ[0x00]: RESET 0x03 SEQ[0x00]: RESET 0x03
|
||||
SEQ[0x01]: CLOCKING 0x01 SEQ[0x01]: CLOCKING 0x01
|
||||
SEQ[0x02]: MAPMASK 0x0F* SEQ[0x02]: MAPMASK 0x0F*
|
||||
SEQ[0x03]: CHARMAP 0x00 SEQ[0x03]: CHARMAP 0x00
|
||||
SEQ[0x04]: MEMMODE 0x06 SEQ[0x04]: MEMMODE 0x06
|
||||
FEAT: 0x02 FEAT: 0x02
|
||||
MISC: 0x23 MISC: 0xA7
|
||||
STATUS0: 0x10 STATUS0: 0x10
|
||||
LATCHES: 0x00000000 LATCHES: 0x00000000
|
||||
ACCESS: 0x1411 ACCESS: 0x0400
|
||||
|
||||
One of the apparent oddities is that, for mode 0x0E, the GRC Mode Register was programmed with 0x11, whereas
|
||||
for mode 0x10, it was programmed with 0x00. Why would mode 0x0E want to set the ODDEVEN bit during the scroll,
|
||||
when it hadn't been set during any other writes to the screen?
|
||||
|
||||
At this point, I dumped the instruction history buffer a bit ("*dh 100*"), and noticed this GRC write:
|
||||
|
||||
C000:1582 8BC5 MOV AX,BP ;history=33
|
||||
C000:1584 B603 MOV DH,03 ;history=32
|
||||
C000:1586 B2CE MOV DL,CE ;history=31
|
||||
C000:1588 E88AF7 CALL 0D15 ;history=30
|
||||
C000:0D15 86C4 XCHG AL,AH ;history=29
|
||||
C000:0D17 EE OUT DX,AL ;history=28
|
||||
C000:0D18 42 INC DX ;history=27
|
||||
C000:0D19 86C4 XCHG AL,AH ;history=26
|
||||
C000:0D1B EE OUT DX,AL ;history=25
|
||||
C000:0D1C 4A DEC DX ;history=24
|
||||
|
||||
So I looked farther back and saw where BP was set:
|
||||
|
||||
C000:1522 BA00A0 MOV DX,A000 ;history=97
|
||||
C000:1525 BD1105 MOV BP,0511 ;history=96
|
||||
|
||||
Here's the complete function:
|
||||
|
||||
GR_ST_1:
|
||||
MOV DX,A000
|
||||
MOV BP,0511
|
||||
CMP AH,0F
|
||||
JC 1535
|
||||
CALL 14F7
|
||||
JNC 1535
|
||||
MOV BP,0501
|
||||
RET
|
||||
|
||||
OK, so any (EGA) graphics mode below 0x0F is going to the trigger the use of Write Mode 1 with the ODDEVEN bit set.
|
||||
And sure enough, the scrolling bug also occurs when using 320x200 16-color mode 0x0D.
|
||||
|
||||
It's also worth noting that, whenever the ODDEVEN bit of the GRC Mode Register is set, the SEQUENTIAL bit in the Sequencer
|
||||
Memory Mode Register is supposed to be clear (and vice versa -- those two bits are supposed to always be oppositely set).
|
||||
But here, the IBM EGA BIOS hasn't done that. One wonders if that was a mistake....
|
||||
|
||||
Another bit of trivia: while dumping the frame buffer in a VGA text mode in a different emulator, I discovered that
|
||||
when I turned off the ODDEVEN bit in the GRC Mode Register, odd bytes would still appear from plane 1; it wasn't until I
|
||||
*also* turned off the CHAIN bit in the GRC Miscellaneous Register that the odd bytes would no longer appear. But,
|
||||
that could have just been an idiosyncrasy of that particular emulator.
|
||||
|
||||
As a result of all these observations, and more importantly, to make EGA scrolling work properly in modes 0x0D and 0x0E,
|
||||
I've changed the Video component to use odd/even memory functions *only* when the SEQUENTIAL bit (bit 2) of the
|
||||
Sequencer's Memory Mode Register is clear, instead of relying on the ODDEVEN bit (bit 4) of the Graphics Controller's Mode
|
||||
Register.
|
||||
|
||||
To be continued.... because I've barely scratched the surface of all the side-effects of EGA/VGA odd/even addressing,
|
||||
and I hope to do some testing on real hardware in the near future.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*June 5, 2015*
|
||||
204
_posts/2015-07-17-windows-95.md
Normal file
204
_posts/2015-07-17-windows-95.md
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
---
|
||||
layout: post
|
||||
title: Windows 95
|
||||
date: 2015-07-17 11:00:00
|
||||
category: Windows 95
|
||||
permalink: /blog/2015/07/17/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: deskpro386
|
||||
debugger: true
|
||||
state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
|
||||
config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/debugger/machine.xml
|
||||
drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
|
||||
autoMount: ''
|
||||
---
|
||||
|
||||
This week (July 14, 2015) was the 20th anniversary of Windows 95 RTM ("Release To Manufacturing"). So I decided to
|
||||
throw a PCjs party and try running Windows 95 Setup inside a PCx86 machine for the first time.
|
||||
|
||||
Sadly, it immediately failed:
|
||||
|
||||
Please wait while Setup initializes.
|
||||
Windows requires a computer with an 80386 processor or higher.
|
||||
|
||||
The failing code:
|
||||
|
||||
0E36:08FD 06 PUSH ES
|
||||
0E36:08FE 1E PUSH DS
|
||||
0E36:08FF 9C PUSHF
|
||||
0E36:0900 33C0 XOR AX,AX
|
||||
0E36:0902 50 PUSH AX
|
||||
0E36:0903 9D POPF
|
||||
0E36:0904 9C PUSHF
|
||||
0E36:0905 58 POP AX
|
||||
0E36:0906 A90080 TEST AX,8000
|
||||
0E36:0909 7517 JNZ 0922
|
||||
0E36:090B B80070 MOV AX,7000
|
||||
0E36:090E 50 PUSH AX
|
||||
0E36:090F 9D POPF
|
||||
0E36:0910 FB STI
|
||||
0E36:0911 9C PUSHF
|
||||
0E36:0912 58 POP AX
|
||||
0E36:0913 A90070 TEST AX,7000
|
||||
0E36:0916 7405 JZ 091D
|
||||
0E36:0918 B88603 MOV AX,0386
|
||||
0E36:091B EB08 JMP 0925
|
||||
0E36:091D B88602 MOV AX,0286
|
||||
0E36:0920 EB03 JMP 0925
|
||||
0E36:0922 B88600 MOV AX,0086
|
||||
0E36:0925 9D POPF
|
||||
0E36:0926 1F POP DS
|
||||
0E36:0927 07 POP ES
|
||||
0E36:0928 C3 RET
|
||||
|
||||
was easily fixed with a change to [x86cpu.js](/modules/pcx86/lib/x86cpu.js), allowing the IOPL bits to be
|
||||
modified in real-mode on an 80386. When I had previously tweaked setPS() to accomodate 80286/80386 discrimination
|
||||
logic in OS/2 1.0, there was no 80386 support in PCx86 at that time, so it was sufficient to *never* allow the
|
||||
IOPL bits to be altered in real-mode.
|
||||
|
||||
The next problem was triggered by Setup's CAB ("Diamond") decompression code, which uses all 32 bits
|
||||
of the 80386's 32-bit registers. That in itself was not a problem, but by leaving stray bits in the upper halves of
|
||||
registers like EDX, it exposed a bug in the PCx86 I/O instruction handlers, which neglected to mask EDX with 0xFFFF before
|
||||
performing port lookups, causing mysterious I/O failures that usually manifested themselves as hard disk I/O errors.
|
||||
|
||||
Then PCx86 crashed in the middle of the decompression of the first CAB file (MINI.CAB). It turned out the stack
|
||||
had been improperly adjusted because a "RETF n" instruction mistakenly believed that a stack switch had occurred.
|
||||
This was, in fact, a left-over condition from a protected-mode stack-switch. That was easily cleared.
|
||||
|
||||
Next, I discovered that I had never finished updating a handful of instructions to full 32-bit operation;
|
||||
namely, INC, DEC, NEG, NOT, TEST, MOVSB and MOVSW. Those are done now.
|
||||
|
||||
Then I ran into a couple of Windows 95 oddities. First, some kernel initialization code deliberately
|
||||
executed an invalid opcode (0x0F,0xFF) and expected its DPMI exception (0x06) handler to field the exception; that was
|
||||
fixed by flagging the opcode as genuinely invalid.
|
||||
|
||||
> SIDEBAR
|
||||
|
||||
> By default, PCx86 marks opcodes as invalid *only* if they have been confirmed invalid. PCx86 considers the vast
|
||||
majority of unused/undocumented opcodes to be merely "undefined" until I've seen them in the wild. An instruction will
|
||||
be marked invalid if I either discover that real hardware treats it as an Invalid Opcode (by throwing the exception)
|
||||
*or* that real software expects it to. For opcode 0x0F,0xFF, it was the latter.
|
||||
|
||||
> Don't be confused that Intel also refers to the Invalid Opcode exception (0x06) as the #UD ("Undefined")
|
||||
opcode exception. Every opcode that triggers that exception is, by definition, defined: it's defined as invalid.
|
||||
I consider it a misnomer to refer to any invalid instruction as "undefined".
|
||||
|
||||
The other recent Windows 95 oddity I ran into was an instruction with multiple address-override (0x67) prefixes; the
|
||||
first prefix changed the instruction's addressing mode from 16-bit to 32-bit, and the second prefix changed it back to
|
||||
16-bit. PCx86 should have simply ignored the second prefix.
|
||||
|
||||
With all of the above changes in place, PCx86 v1.18.4 is able to run Windows 95 Setup a bit farther, but still far from
|
||||
completion. If you want to give it a spin yourself, start the machine below (click the "Run" button) and once it has
|
||||
finished booting, run SETUP from drive B, where the first Windows 95 diskette is already loaded.
|
||||
|
||||
If, when it crashes (and it will), you're interested in examining the instructions that were executed prior to the
|
||||
crash, you can dump the instruction history buffer using the Debugger's "dh" command.
|
||||
|
||||
NOTE: The diskette images contain a pre-release version of Windows 95, as I don't currently have the RTM version on
|
||||
diskette.
|
||||
|
||||
---
|
||||
|
||||
August 13, 2015 Update
|
||||
---
|
||||
|
||||
PCx86 v1.18.8 has made a little more progress running Windows 95 Setup, but CAB decompression still fails almost
|
||||
immediately. To monitor DOS calls until the first 36-byte read of PRECOPY1.CAB, try setting the following
|
||||
breakpoint and then starting the machine, using the PCx86 Debugger *input* field next to the **Enter** button:
|
||||
|
||||
m dos off
|
||||
bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"
|
||||
g
|
||||
|
||||
Alternatively, you can hard-code those commands into the Debugger component of the machine.xml file; eg:
|
||||
|
||||
```xml
|
||||
<debugger id="debugger" messages="fault|tss|int" commands='m dos off;bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"'/>
|
||||
```
|
||||
|
||||
Once the 36-byte read is hit, you'll probably want to stop on the next instruction that examine those bytes,
|
||||
by using a memory read breakpoint:
|
||||
|
||||
br ds:dx
|
||||
|
||||
and you also might want to change the first breakpoint to stop on *any* file read, and add a second breakpoint
|
||||
to dump the initial contents of those reads:
|
||||
|
||||
bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f"
|
||||
bp FDC8:422A "if fn==3f;di ds:dx;db ds:dx;h;else"
|
||||
|
||||
In the current machine, `1ED4:16B4` is the DOS INT 0x21 entry point and `FDC8:422A` is the corresponding IRET.
|
||||
The first breakpoint sets an internal variable, `fn`, to the value of the **AH** register on entry, so that the
|
||||
second breakpoint can check the value on exit.
|
||||
|
||||
Regarding the other Debugger commands shown above, the `dos` command describes the current DOS operation
|
||||
(alternatively, you could use the `m int on; m dos on` commands to turn on DOS interrupt messages). The `di`
|
||||
command dumps PCx86 BACKTRACK(tm) information, to help you visually confirm which INT 0x21 calls are reading
|
||||
PRECOPY1.CAB. Finally, the `if` command evaluates the given expression, and if the result is non-zero ("true"),
|
||||
all subsequent commands are executed, up to to any `else` command; otherwise, only commands after the `else`
|
||||
command will be executed, and if there is no `else` command, execution will stop.
|
||||
|
||||
Debugger expressions may contain the usual variety of arithmetic, bitwise and logical binary operators, and they
|
||||
are evaluated using traditional operator precedence (ie, the same as C or JavaScript); any other operators, such as
|
||||
parentheses, assignment operators, and unary or ternary operators, are not supported in expressions.
|
||||
|
||||
This test machine below has been updated to load WDEB386.EXE prior to starting B:SETUP.EXE, if you prefer using
|
||||
WDEB386. Make sure the machine is running (ie, click the **Run** button, or use the PCx86 Debugger "g" command),
|
||||
and then click on the Debugger *output* control to give it focus and press CTRL-C to trigger WDEB386.
|
||||
|
||||
The Debugger *input* field is used exclusively for PCx86 Debugger commands, whereas the *output* textarea combines
|
||||
all Debugger output *and* WDEB386 COM2 serial port I/O. You can even use the PCx86 Debugger to debug
|
||||
the WDEB386 debugger; just make sure the appropriate text control has focus before typing a command.
|
||||
|
||||
To help reduce confusion, the PCx86 Debugger displays a double-character command prefix, to differentiate its commands
|
||||
from WDEB386's single-character command prompt -- but it's still easy to get confused.
|
||||
|
||||
A quick recap of those command prefixes (which you won't see until AFTER you've typed a PCx86 command):
|
||||
|
||||
* `>>` indicates real-mode
|
||||
* `##` indicates protected-mode
|
||||
* `--` indicates V86-mode
|
||||
|
||||
80386 Debug register (DR0-DR7) support was recently added, so even WDEB386 read/write breakpoints should work now.
|
||||
|
||||
---
|
||||
|
||||
August 21, 2015 Update
|
||||
---
|
||||
|
||||
PCx86 v1.19.1 has finally solved a number of nagging bugs. The CAB decompression code itself was running
|
||||
fine; it would crash after loading a 32-bit value into EAX *and* a timer interrupt occurred. A path
|
||||
through the interrupt handler was trashing the upper bits of EAX. The culprit: any of the MOV instructions
|
||||
that move an immediate value into one of the "high" 8-bit registers (AH, BH, CH, or DH). Those instructions
|
||||
were failing to preserve the upper 16 bits of the entire register. Further proof that the most exasperating
|
||||
bugs sometimes have the most mundane causes.
|
||||
|
||||
Also recently fixed: the annoying VGA video glitch that would occur whenever the mouse was moved, improper
|
||||
updates to the ACCESSED bit in a selector's descriptor table entry, improper error codes when a selector load
|
||||
generated a fault, and the RETF instruction's failure to properly restart when the return address referred
|
||||
to a not-present segment.
|
||||
|
||||
The next problem appears to be timer-related. When Windows 95 Setup begins its hardware analysis, it
|
||||
gets "stuck" in code that's reading and writing timer ports (0x40 and 0x43). Turning on timer port messages
|
||||
with the `"m port on;m timer on"` Debugger commands reveals that the problem maybe an unsupported timer
|
||||
command:
|
||||
|
||||
chipset.outPort(0x0043,PIT1_CTRL,0xD2) @1847:DD02
|
||||
PIT1_CTRL: Read-Back command not supported (yet)
|
||||
|
||||
---
|
||||
|
||||
September 12, 2015 Update
|
||||
---
|
||||
|
||||
Lots of bugs have been squashed in the past few weeks -- not enough to finish setting up Windows 95, but it's getting
|
||||
closer. More details on recent releases can be found [here](https://github.com/jeffpar/pcjs/releases).
|
||||
|
||||
The machine below has been reconfigured with a hard disk image containing all the Windows 95 Setup files. The machine
|
||||
is at the point where Windows 95 SETUP has just rebooted.
|
||||
|
||||
{% include machine.html id="deskpro386" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*July 17, 2015 (updated September 12, 2015)*
|
||||
39
_posts/2015-09-21-windows-95-in-your-web-browser.md
Normal file
39
_posts/2015-09-21-windows-95-in-your-web-browser.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
layout: post
|
||||
title: Windows 95 In Your Web Browser
|
||||
date: 2015-09-21 11:00:00
|
||||
category: Windows 95
|
||||
permalink: /blog/2015/09/21/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: deskpro386
|
||||
state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
|
||||
config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/machine.xml
|
||||
drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
|
||||
autoMount: ''
|
||||
---
|
||||
|
||||
Today, the last serious bug preventing a successful boot of Windows 95 was fixed. I won't bore you with
|
||||
the details.
|
||||
|
||||
OK, I will: three arithmetic instructions (specifically, **AND**, **OR** and **XOR**) include a variation that
|
||||
converts an immediate signed byte into a signed word. Those variations were failing to truncate the result when
|
||||
a 16-bit operand size was in effect, and if the destination was a register, the upper 16 bits of that register
|
||||
could become corrupted.
|
||||
|
||||
The [Windows 95 Test Machine](/disks/pcx86/windows/win95/4.00.950/) hard disk has been updated
|
||||
with a complete set of Windows 95 files from a "Compact" installation, and first boot has finished, so instead
|
||||
of the initial "Getting ready to run Windows 95 for the first time..." splash screen, you'll see the normal
|
||||
Windows 95 startup screen.
|
||||
|
||||
The machine is still a bit finicky. It easily gets confused about the state of its shift keys if you switch away
|
||||
from the browser and then back again. And Explorer windows don't open in the correct view; for example, both
|
||||
**My Computer** and **Recycle Bin** open the same (incorrect) view. In short, there are still some serious bugs
|
||||
to be resolved, but booting has been achieved.
|
||||
|
||||
The adventure continues.
|
||||
|
||||
{% include machine.html id="deskpro386" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*September 21, 2015*
|
||||
218
_posts/2015-10-27-windows-95-and-early-80386-cpus.md
Normal file
218
_posts/2015-10-27-windows-95-and-early-80386-cpus.md
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
---
|
||||
layout: post
|
||||
title: Windows 95 and Early 80386 CPUs
|
||||
date: 2015-10-27 11:00:00
|
||||
categories: ['Windows 95', '80386']
|
||||
permalink: /blog/2015/10/27/
|
||||
---
|
||||
|
||||
Every time Windows 95 starts up, its real-mode loader performs the following CPU identification test.
|
||||
|
||||
&0654:121E 9C PUSHF
|
||||
&0654:121F 33C0 XOR AX,AX ; try to clear bit 15 of flags
|
||||
&0654:1221 50 PUSH AX
|
||||
&0654:1222 9D POPF
|
||||
&0654:1223 9C PUSHF
|
||||
&0654:1224 58 POP AX
|
||||
&0654:1225 A90080 TEST AX,8000 ; bit 15 of flags set anyway?
|
||||
&0654:1228 7543 JNZ 126D ; yes (must be an 8086/8086)
|
||||
&0654:122A B80070 MOV AX,7000 ; try to set bits 12-14 of flags
|
||||
&0654:122D 50 PUSH AX
|
||||
&0654:122E 9D POPF
|
||||
&0654:122F FB STI
|
||||
&0654:1230 9C PUSHF
|
||||
&0654:1231 58 POP AX
|
||||
&0654:1232 9D POPF
|
||||
&0654:1233 A90070 TEST AX,7000 ; any of bits 12-14 of flags set?
|
||||
&0654:1236 7436 JZ 126E ; no (must be an 80286)
|
||||
&0654:1238 51 PUSH CX
|
||||
&0654:1239 33C9 XOR CX,CX
|
||||
&0654:123B 8BC4 MOV AX,SP
|
||||
&0654:123D 83E003 AND AX,0003
|
||||
&0654:1240 7404 JZ 1246
|
||||
&0654:1242 8BC8 MOV CX,AX
|
||||
&0654:1244 2BE0 SUB SP,AX
|
||||
&0654:1246 669C PUSHFD
|
||||
&0654:1248 666800000400 PUSH 00040000 ; try to set the AC bit of flags
|
||||
&0654:124E 669D POPFD
|
||||
&0654:1250 669C PUSHFD
|
||||
&0654:1252 6658 POP EAX
|
||||
&0654:1254 669D POPFD
|
||||
&0654:1256 66A900000400 TEST EAX,00040000 ; AC bit set?
|
||||
&0654:125C 7508 JNZ 1266 ; yes (must be an 80486, or newer)
|
||||
&0654:125E 40 INC AX
|
||||
&0654:125F 03E1 ADD SP,CX
|
||||
&0654:1261 59 POP CX
|
||||
&0654:1262 0BC0 OR AX,AX
|
||||
&0654:1264 F9 STC ; return ZF clear and CF set to indicate 80386
|
||||
&0654:1265 C3 RET
|
||||
&0654:1266 03E1 ADD SP,CX
|
||||
&0654:1268 59 POP CX
|
||||
&0654:1269 660BC0 OR EAX,EAX ; return ZF clear and CF clear to indicate 80486
|
||||
&0654:126C C3 RET
|
||||
&0654:126D 9D POPF
|
||||
&0654:126E 33C0 XOR AX,AX ; return ZF set to indicate 80286 or older
|
||||
&0654:1270 C3 RET
|
||||
|
||||
If the above function returns ZF set, then the processor is an 80286 or older, so Windows 95 displays the
|
||||
following message and exits:
|
||||
|
||||
You need an 80386 processor to run Windows.
|
||||
|
||||
If the above function returns CF set, then the processor is an 80386, and if CF is clear, the processor is an
|
||||
80486 or newer.
|
||||
|
||||
When CF is set, Windows 95 proceeds to the following 80386 stepping check, which attempts to execute an
|
||||
XBTS (Extract Bit String) instruction -- an instruction that existed only on B0 and earlier 80386 steppings.
|
||||
|
||||
&0654:1299 53 PUSH BX
|
||||
&0654:129A 51 PUSH CX
|
||||
&0654:129B 52 PUSH DX
|
||||
&0654:129C B80635 MOV AX,3506 ; save the current "invalid opcode" handler (IVT entry #6)
|
||||
&0654:129F CD21 INT 21
|
||||
&0654:12A1 8CC0 MOV AX,ES
|
||||
&0654:12A3 66C1E010 SHL EAX,10
|
||||
&0654:12A7 8BC3 MOV AX,BX
|
||||
&0654:12A9 6650 PUSH EAX
|
||||
&0654:12AB 1E PUSH DS
|
||||
&0654:12AC BAE012 MOV DX,12E0
|
||||
&0654:12AF 8CCB MOV BX,CS
|
||||
&0654:12B1 8EDB MOV DS,BX
|
||||
&0654:12B3 B80625 MOV AX,2506 ; temporarily install a new "invalid opcode" exception handler
|
||||
&0654:12B6 CD21 INT 21
|
||||
&0654:12B8 1F POP DS
|
||||
&0654:12B9 33C0 XOR AX,AX
|
||||
&0654:12BB 8BD0 MOV DX,AX
|
||||
&0654:12BD B900FF MOV CX,FF00
|
||||
&0654:12C0 0FA6CA XBTS CX,DX,AX,CL ; attempt to execute an XBTS instruction
|
||||
&0654:12C3 6658 POP EAX
|
||||
&0654:12C5 8BD0 MOV DX,AX
|
||||
&0654:12C7 66C1E810 SHR EAX,10
|
||||
&0654:12CB 1E PUSH DS
|
||||
&0654:12CC 8ED8 MOV DS,AX
|
||||
&0654:12CE B80625 MOV AX,2506 ; restore the original "invalid opcode" exception handler
|
||||
&0654:12D1 CD21 INT 21
|
||||
&0654:12D3 1F POP DS
|
||||
&0654:12D4 B8B000 MOV AX,00B0
|
||||
&0654:12D7 0BC9 OR CX,CX ; did XBTS work (ie, did CX change)?
|
||||
&0654:12D9 7401 JZ 12DC ; yes, so we must have a B0 stepping or earlier
|
||||
&0654:12DB 40 INC AX ; no, so bump the stepping to B1
|
||||
&0654:12DC 5A POP DX
|
||||
&0654:12DD 59 POP CX
|
||||
&0654:12DE 5B POP BX
|
||||
&0654:12DF C3 RET ; returns AX == 0xB0 if B0 stepping or earlier, 0xB1 if B1 stepping or later
|
||||
|
||||
&0654:12E0 55 PUSH BP ; temporary "invalid opcode" handler
|
||||
&0654:12E1 8BEC MOV BP,SP
|
||||
&0654:12E3 83460203 ADD [BP+02],0003 ; advance IP past the 3-byte XBTS instruction
|
||||
&0654:12E7 5D POP BP
|
||||
&0654:12E8 CF IRET
|
||||
|
||||
If the above function returns 0xB0, then the 80386 is a B0 or earlier stepping, so Windows 95 displays the
|
||||
following message and aborts:
|
||||
|
||||
Windows may not run correctly with the 80386 processor that is installed in this computer.
|
||||
Upgrade your 80386 processor.
|
||||
|
||||
Otherwise, the 80386 is a B1 or later stepping, so Windows 95 next performs a multiplication test (a simplified
|
||||
version of the multiplication tests discussed in "[Early 80386 CPUs](/blog/2015/02/23/)"):
|
||||
|
||||
&0654:12E9 33C9 XOR CX,CX
|
||||
&0654:12EB 66BB81000000 MOV EBX,00000081
|
||||
&0654:12F1 66B800A01704 MOV EAX,0417A000
|
||||
&0654:12F7 66F7E3 MUL EBX
|
||||
&0654:12FA 6683FA02 CMP EDX,00000002
|
||||
&0654:12FE 750B JNZ 130B
|
||||
&0654:1300 663D00A0E70F CMP EAX,0FE7A000
|
||||
&0654:1306 7503 JNZ 130B
|
||||
&0654:1308 E2E1 LOOP 12EB
|
||||
&0654:130A C3 RET
|
||||
|
||||
If any of the 65,536 identical multiplications return an incorrect result, Windows 95 displays the following
|
||||
message:
|
||||
|
||||
WARNING: The 80386 processor in this computer may not reliably execute 32-bit
|
||||
multiplication. Windows may occasionally fail on this computer.
|
||||
|
||||
You may want to replace your 80386 processor.
|
||||
Press any key to continue...Press a key to continue
|
||||
|
||||
A multiplication failure implies that the 80386 stepping is B1, because later steppings resolved the problem.
|
||||
|
||||
You may have heard that Windows 95 [pulled support for the 80386 B1 stepping](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx),
|
||||
and that's true, but only insofar as Windows 95 SETUP is concerned. The following code is executed by WINSETUP.BIN,
|
||||
a 16-bit Windows component that manages the Windows 95 installation process:
|
||||
|
||||
#05C7:69E8 1E PUSH DS
|
||||
#05C7:69E9 07 POP ES
|
||||
#05C7:69EA 6657 PUSH EDI
|
||||
#05C7:69EC FD STD
|
||||
#05C7:69ED 66BF00000000 MOV EDI,00000000
|
||||
#05C7:69F3 678A07 MOV AL,[EDI]
|
||||
#05C7:69F6 67AA STOSB
|
||||
#05C7:69F8 33C0 XOR AX,AX
|
||||
#05C7:69FA 6681FFFFFF0000 CMP EDI,0000FFFF
|
||||
#05C7:6A01 7501 JNZ 6A04
|
||||
#05C7:6A03 40 INC AX
|
||||
#05C7:6A04 FC CLD
|
||||
#05C7:6A05 665F POP EDI
|
||||
#05C7:6A07 C3 RET
|
||||
|
||||
Ths above code checks for B1 stepping [Errata #7](/pubs/pc/reference/intel/80386/#b1-errata): "Wrong Register Size for
|
||||
String Instructions in Mixed 16/32-bit Addressing Systems." It returns AX == 0 if the STOSB instruction updated EDI
|
||||
correctly (0xFFFFFFFF) or AX == 1 if EDI is incorrect (0x0000FFFF).
|
||||
|
||||
If Errata #7 is detected, then Windows 95 SETUP displays the following message and aborts:
|
||||
|
||||
Setup Error B1: Setup has detected an 80386 processor that is not compatible with this version of Windows.
|
||||
Before you can run this version of Windows, you need to upgrade your processor.
|
||||
Contact your computer manufacturer for more information.
|
||||
|
||||
However, if you can get through SETUP, Windows 95 will still run on a B1 stepping. For example, if you installed
|
||||
Windows 95 using a newer 80386, and then later "downgraded" the CPU to a B1, Windows 95 would still run. If your B1
|
||||
suffered from the multiplication flaw, you would see the 32-bit multiplication warning on start-up, but you could
|
||||
still continue to run, and if there was no multiplication problem, you would not see any message at all.
|
||||
|
||||
---
|
||||
|
||||
PCjs v1.20.0 now supports a "stepping" attribute on the <cpu> element, which you can use to simulate specific
|
||||
stepping behavior. For example, a *machine.xml* file with the following CPU definition:
|
||||
|
||||
```xml
|
||||
<cpu id="cpu386" model="80386" stepping="b0"/>
|
||||
```
|
||||
|
||||
will cause Windows 95 to abort exactly as described as above. Similarly, selecting a 80386 B1 stepping:
|
||||
|
||||
```xml
|
||||
<cpu id="cpu386" model="80386" stepping="b1"/>
|
||||
```
|
||||
|
||||
will cause Windows 95 to display the 32-bit multiplication warning shown above (PCjs deliberately fails the exact
|
||||
multiplication test that Windows 95 performs).
|
||||
|
||||
If you want to simulate a B1 stepping that does *not* have the 32-bit multiplication flaw, set the stepping to B2:
|
||||
|
||||
```xml
|
||||
<cpu id="cpu386" model="80386" stepping="b2"/>
|
||||
```
|
||||
|
||||
B2 was not an actual 80386 stepping; it is a *pseudo-stepping* that provides a simple way of specifying a B1 80386 that
|
||||
passes all 32-bit multiplication tests.
|
||||
|
||||
As previously discussed, Windows 95 SETUP will refuse to install on any "A" or "B" stepping, but if it's already been
|
||||
installed, it *will* start up on a B1 stepping.
|
||||
|
||||
PCjs stepping support is extremely limited at this point. Here's a summary:
|
||||
|
||||
1. 80386 steppings A0-B0 provide *limited* support for the short-lived XBTS and IBTS instructions
|
||||
2. 80386 steppings A0-B1 enable [Errata #7](/pubs/pc/reference/intel/80386/#b1-errata) for STOSB (as tested by Windows 95; see above)
|
||||
3. 80386 stepping B1 enables 32-bit multiplication errors (as tested by Windows 95; see above)
|
||||
4. 80386 stepping B2 includes all supported B1 errata, but without 32-bit multiplication errors
|
||||
|
||||
In addition, on 80386 reset, we set the CPU revision number in DX to the appropriate value for the specified stepping.
|
||||
|
||||
Support for additional 80286 and 80386 errata may be added over time, as interesting scenarios or test cases are discovered.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*October 27, 2015*
|
||||
124
_posts/2015-12-10-rebuilding-the-pcjs-website.md
Normal file
124
_posts/2015-12-10-rebuilding-the-pcjs-website.md
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
---
|
||||
layout: post
|
||||
title: Rebuilding the PCjs Website
|
||||
date: 2015-12-10 11:03:00
|
||||
category: Website
|
||||
permalink: /blog/2015/12/10/
|
||||
---
|
||||
|
||||
It's been nice using Node.js to power the PCjs website, using Amazon's Elastic Beanstalk service, but that combination
|
||||
has also been a source of some frustrations.
|
||||
|
||||
+ When someone posts an article or a tweet linking to a PCjs page, the website bogs down, and while Amazon's Elastic
|
||||
Beanstalk service makes it easy to automatically scale up, each new instance automatically multiplies my expenses as well,
|
||||
which hover around $34/month for a single instance. With assorted S3 and transfer charges, my average monthly bill is
|
||||
over $55/month. That's a bit much for a site that generates zero revenue.
|
||||
|
||||
+ Once or twice a year, when I'm attempting to either update the server or upgrade my Node configuration, the update
|
||||
or upgrade will fail, and Amazon's web console provides virtually no details about why it failed. I get an error message
|
||||
like "**ERROR: Failed to deploy application**" and that is it. Literally.
|
||||
|
||||
Granted, there are some "simple" things I could do to improve performance, like adding an **nginx** proxy server to
|
||||
the configuration, but that feels like a band-aid solution, and as a software developer, the time spent fiddling with
|
||||
web server issues is time I'd much rather spend writing code.
|
||||
|
||||
Since PCjs is designed to do all its work in the user's web browser, and since the website can be completely built out
|
||||
as a set of static web pages, I've decided to stop using Node to power www.pcjs.org. I'm in the process of migrating
|
||||
the website to [GitHub Pages](https://pages.github.com/), and using [Jekyll](https://help.github.com/articles/using-jekyll-with-pages/)
|
||||
to convert all my existing Markdown files to HTML.
|
||||
|
||||
This new approach is *very* similar to what the PCjs custom Node modules did: every time someone visited a folder
|
||||
on the website that did not yet contain an "index.html", the PCjs Node server would create one, either by converting the
|
||||
README.md file in that folder to HTML or generating a default HTML document. The PCjs Markdown-to-HTML converter also
|
||||
contained some special logic that made it easy to embed PCjs machines on a page.
|
||||
|
||||
Thanks to GitHub Pages, all of that now happens ahead of time: whenever I update the PCjs "gh-pages" branch on GitHub,
|
||||
Jekyll automatically runs through the entire site and rebuilds a complete set of web pages.
|
||||
|
||||
The Node web server would automatically embed PCjs machines on web pages by looking for special Markdown links, such as;
|
||||
|
||||
[Embedded IBM PC](machine.xml "PCjs:ibm5150")
|
||||
|
||||
After migrating to GitHub Pages and Jekyll, that markup must now be written as:
|
||||
|
||||
{% raw %}
|
||||
{% include machine.html id="ibm5150" %}
|
||||
{% endraw %}
|
||||
|
||||
and the following "Front Matter" (YAML) must appear at the top of the Markdown file:
|
||||
|
||||
---
|
||||
...
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: ibm5150
|
||||
---
|
||||
|
||||
Basically, the YAML at the top of the file lists all the machines that the page intends to use, and then
|
||||
{% raw %}`{% include ... %}`{% endraw %} is inserted in the text at the point where a machine should be embedded.
|
||||
|
||||
The `machine.html` include accepts only one parameter: the `id` of a machine listed at the top of the file.
|
||||
|
||||
The `machines` element at the top of the file must specify a `type` and `id` at a minimum. `type` should be one of
|
||||
the following values, depending on whether you want an IBM PC or Challenger 1P:
|
||||
|
||||
- pc
|
||||
- c1p
|
||||
|
||||
and `id` can be any identifier you want to use to embed the machine. If you want to include the machine's built-in
|
||||
debugger, set `debugger` to *true*; the older method of specifying *pc-dbg* or *c1p-dbg* instead of *pc* or *c1p* as the
|
||||
`type` still works, too.
|
||||
|
||||
You may also use `config` to specify a machine XML configuration file if not using the default *machine.xml*;
|
||||
`template` to specify an alternate XSL template file if not using the default *components.xsl*; `state` to specify
|
||||
a JSON-encoded machine state file if the machine requires a predefined state; and `uncompiled` may be set to *true*
|
||||
to force a machine to use uncompiled sources, overriding the value of `site.pcjs.compiled` in **_config.yml**.
|
||||
|
||||
For example, the PCjs home page contains two machines, so this appears at the top of the
|
||||
[Markdown file](https://raw.githubusercontent.com/jeffpar/pcjs/master/index.md):
|
||||
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: ibm5150
|
||||
config: /devices/pcx86/machine/5150/mda/64kb/machine.xml
|
||||
- type: c1p
|
||||
id: demoC1P
|
||||
config: /devices/c1p/machine/8kb/large/machine.xml
|
||||
|
||||
If necessary, you can also override some of the settings in a machine XML file. Here's an example of overriding the
|
||||
FDC `autoMount` setting, making it easy to reuse the same machine XML file with different boot disks:
|
||||
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: deskpro386
|
||||
debugger: true
|
||||
autoMount:
|
||||
A:
|
||||
path: /disks/pcx86/os2/misc/football/FOOTBALL-76817.json
|
||||
config: /devices/pcx86/machine/compaq/deskpro386/ega/4096kb/debugger/machine.xml
|
||||
|
||||
Other settings that can currently be overridden include:
|
||||
|
||||
+ `autoPower`
|
||||
+ `drives`
|
||||
+ `messages`
|
||||
+ `state`
|
||||
|
||||
Additional overrides will be added as needed. See the [Windows 95 Demo](/disks/pcx86/windows/win95/4.00.950/)
|
||||
machine and its associated [Markdown file](https://raw.githubusercontent.com/jeffpar/pcjs/master/disks/pcx86/windows/win95/4.00.950/README.md)
|
||||
for more override examples, including how to set `autoMount` to *not* mount any diskettes.
|
||||
|
||||
I will continue to include a Node web server with the PCjs Project, but it's now intended for development
|
||||
purposes only (not production servers). I've updated the PCjs MarkOut component to parse any "Front Matter"
|
||||
at the top of the PCjs Markdown files, and to convert all new Jekyll-style embedded machines and screenshots
|
||||
to the older Markdown-compatible formats, so pages generated by the Node web server should still function
|
||||
as before. But there are no guarantees.
|
||||
|
||||
WARNING: If you decide to run/alternate between Jekyll's web server (WEBrick) and the built-in Node web
|
||||
server (server.js), you should run "grunt clean" before starting either one, to remove any old **index.html**
|
||||
files. Node may inadvertently reuse old "index.html" files, and Jekyll may inadvertently propagate them
|
||||
to its "_site" folder. It's easy to tell when this happens, because you'll see the wrong color scheme: Node
|
||||
web server pages were designed to use *dark* colors, whereas Jekyll web server pages currently use *light* colors.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*December 10, 2015*
|
||||
48
_posts/2015-12-27-revisiting-os2.md
Normal file
48
_posts/2015-12-27-revisiting-os2.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
layout: post
|
||||
title: Revisiting OS/2
|
||||
date: 2015-12-27 11:00:00
|
||||
category: OS/2
|
||||
permalink: /blog/2015/12/27/
|
||||
machines:
|
||||
- type: pcx86
|
||||
id: ibm5170
|
||||
debugger: true
|
||||
config: /devices/pcx86/machine/5170/ega/2048kb/rev3/debugger/machine.xml
|
||||
drives: '[{name:"20Mb Hard Disk",type:2,path:"/disks/pcx86/fixed/20mb/IBMOS210-EGA.json"}]'
|
||||
automount: ''
|
||||
---
|
||||
|
||||
Just for fun (because I have a warped sense of fun), I decided to revisit some of the old OS/2 software I wrote
|
||||
almost 30 years ago. But first, I needed an OS/2 development environment.
|
||||
|
||||
So I started with a clean install of [IBM OS/2 1.0](/disks/pcx86/os2/ibm/1.0/) in the 8Mhz IBM PC AT machine
|
||||
below, by booting from the "IBM OS/2 1.0 (1.44M Install)" diskette in drive A and reformatting the machine's 20Mb
|
||||
drive C.
|
||||
|
||||
Next, I installed the [MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/). This SDK was released
|
||||
in December 1987 along with [Microsoft OS/2 1.0](/disks/pcx86/os2/microsoft/1.0/). I don't have any of the
|
||||
printed documentation that came with the SDK, such as the *Installation Guide*, but I do have the
|
||||
[Microsoft® Operating System/2 Programmer’s Toolkit](/pubs/pc/software/os2/microsoft/ptk10/) documentation
|
||||
from March 1988, thanks to the [OS/2 Museum](http://www.os2museum.com/wp/os2-history/os2-library/os2-1-x-programming/).
|
||||
|
||||
Aside from **Microsoft Macro Assembler 5.00A** (MASM) and **Microsoft C Compiler 5.10 (Beta)** (CL), the SDK
|
||||
included some other useful tools, such as the **SDK Editor** (SDKED), which was essentially an OS/2 port of
|
||||
Mark Zbikowski's full-screen editor (Z) that was used internally at Microsoft for many years. It was renamed
|
||||
to the **Microsoft Editor** (M or MEP) with the release of **Microsoft C Compiler 5.10**, and it was later integrated
|
||||
into **Programmer's Workbench** (PWB), the text-mode Integrated Development Environment (IDE) that came with
|
||||
**Microsoft C Compiler 6.0**.
|
||||
|
||||
With the introduction of graphical IDEs, such as Visual BASIC in 1991, Visual C++ in 1993, and Visual Studio in 1995,
|
||||
this stand-alone, text-mode editor became obsolete, but in the 1980s, it was a valuable tool. You can learn more
|
||||
about [SDKED](/disks/pcx86/tools/microsoft/os2/sdk/1.02/#using-sdked) on the
|
||||
[MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/) page.
|
||||
|
||||
Our [IBM OS/2 1.0](/disks/pcx86/os2/ibm/1.0/) demo machine (shown below) has the
|
||||
[MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/) pre-installed, so check out our copy of the
|
||||
[Microsoft® Operating System/2 Programmer’s Toolkit](/pubs/pc/software/os2/microsoft/ptk10/) and then write some code!
|
||||
|
||||
{% include machine.html id="ibm5170" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*December 27, 2015*
|
||||
39
_posts/2016-01-23-early-os2-artifacts.md
Normal file
39
_posts/2016-01-23-early-os2-artifacts.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
layout: post
|
||||
title: Early OS/2 Artifacts
|
||||
date: 2016-01-23 14:00:00
|
||||
category: OS/2
|
||||
permalink: /blog/2016/01/23/
|
||||
---
|
||||
|
||||
Before OS/2 was named **OS/2** by IBM on April 2, 1987, the operating system was known by many different names at
|
||||
Microsoft as it evolved, including **DOS5**, **MT-DOS**, **CP-DOS**, and **ADOS**.
|
||||
|
||||
In late 1986, Microsoft began working on a couple different branches. One was called **SIZZLE**, where a variety of
|
||||
performance improvements were tested before being merged back into the main branch.
|
||||
|
||||
Another branch was **FOOTBALL** (aka **PIGSKIN**), an early 80386-based prototype intended to test the viability
|
||||
of the running multiple DOS applications in V86-mode. Sometimes this 80386 version was also called **386DOS**,
|
||||
to distinguish it from **286DOS**. More details are in this
|
||||
[FOOTBALL Design Document](/disks/pcx86/os2/misc/football/87058/#football-design-document).
|
||||
|
||||
To shed some light on those efforts, I recently added a few [OS/2 Prototype Disks](/disks/pcx86/os2/misc/): a small
|
||||
collection of early (mostly pre-1.0) OS/2 boot disks that provide a glimpse of what some of those early OS/2 builds
|
||||
looked like.
|
||||
|
||||
Getting these early versions of OS/2 to run in **PCjs** has been a bit of a challenge. There have been some successes
|
||||
but also some lingering issues. Debugging continues.
|
||||
|
||||
Part of the problem is that these pre-1.0 builds still contain a few bugs. Also, the original
|
||||
[OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/) from February 1987 was developed and
|
||||
tested exclusively on Compaq DeskPro 386 machines from late 1986, so it has some uncommon 80386 dependencies:
|
||||
|
||||
* The [80386 LOADALL](/pubs/pc/reference/intel/80386/loadall/) instruction
|
||||
* 32-bit segment register writes must modify only 16 bits of memory
|
||||
|
||||
**FOOTBALL** also had some specific video hardware requirements: CGA or EGA. Note that the VGA, which is what most
|
||||
emulators use by default these days, did not exist in 1986. The VGA was introduced in April 1987, when IBM
|
||||
unveiled their new PS/2 hardware line -- and announced OS/2.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*January 23, 2016*
|
||||
57
_posts/2016-02-08-super-bowl-winner-pcjs.md
Normal file
57
_posts/2016-02-08-super-bowl-winner-pcjs.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
layout: post
|
||||
title: "Super Bowl Winner: PCjs"
|
||||
date: 2016-02-08 14:00:00
|
||||
permalink: /blog/2016/02/08/
|
||||
---
|
||||
|
||||
The new release of PCjs (v1.20.8) is a fairly minor update, but it's an important one for **FOOTBALL** fans, resolving
|
||||
two annoying problems with the [OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/): mysterious hard-error popups
|
||||
and blank screens.
|
||||
|
||||
A hard-error popup would occur when FOOTBALL tried to initialize a non-existent PRN device. To resolve that, PCjs
|
||||
now provides basic parallel port emulation, in the form of a [ParallelPort](/docs/pcx86/parallel/) component that you
|
||||
include in a machine XML file with the <parallel> element, in much the same way you include the
|
||||
[SerialPort](/docs/pcx86/serial/) component with the <serial> element. This [Compaq DeskPro 386]
|
||||
(/devices/pcx86/machine/compaq/deskpro386/ega/4096kb/debugger/) machine used to run FOOTBALL has now been updated to
|
||||
include one parallel port.
|
||||
|
||||
The other problem was that switching between sessions with the **SysReq** key would often result in a blank screen;
|
||||
the new session was active, but you couldn't see it. This was a side-effect of how FOOTBALL reprograms the video
|
||||
controller when switching screens *and* the linear page mappings it creates to access video memory, which in turn
|
||||
exposed a PCjs memory-management bug. The upshot is that whenever the Video component moves the address of the video
|
||||
buffer (which is *physical* memory), it must tell the CPU to flush any linear-to-physical mappings that may still refer
|
||||
to the old physical memory.
|
||||
|
||||
With these changes, the [OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/) appears to be quite usable now.
|
||||
Feel free to give it a few kicks!
|
||||
|
||||
---
|
||||
|
||||
I've also tidied up a few things in the [Devices](/devices/) folder. ROM images used to be stored under
|
||||
`/devices/pcx86/basic/` and `/devices/pcx86/bios/`, but BASIC and BIOS ROM images aren't actually devices; they are the
|
||||
*contents* of ROM devices. So, with that in mind, I've made the following rearrangements:
|
||||
|
||||
* `/devices/pcx86/bios/5150/*` => `/devices/pcx86/rom/5150/*`
|
||||
* `/devices/pcx86/bios/5160/*` => `/devices/pcx86/rom/5160/*`
|
||||
* `/devices/pcx86/bios/5170/*` => `/devices/pcx86/rom/5170/*`
|
||||
* `/devices/pcx86/bios/compaq/*` => `/devices/pcx86/rom/compaq/*`
|
||||
* `/devices/pcx86/basic/ibm-basic-1.00.json` => `/devices/pcx86/rom/5150/basic/BASIC100.json`
|
||||
* `/devices/pcx86/basic/ibm-basic-1.10.json` => `/devices/pcx86/rom/5160/basic/BASIC110.json`
|
||||
|
||||
This structure mirrors what was done with Machine and Video devices, where the devices are organized
|
||||
first by manufacturer (IBM or COMPAQ) and then by type (MDA, CGA, EGA, etc).
|
||||
|
||||
Here, the first ROM subdivision is either a manufacturer or a machine model. A model implies a manufacturer;
|
||||
for example, models 5150 through 5170 refer to IBM PC models.
|
||||
|
||||
---
|
||||
|
||||
The project currently includes only two BASIC ROM versions, C1.00 and C1.10, which were initially released with the
|
||||
first model 5150 and 5160 machines, respectively. For more details, see [IBM PC ROMs](/devices/pcx86/rom/).
|
||||
|
||||
I believe there was also a BASIC ROM version 1.20 released for IBM PCjr, but since PCjs does not yet emulate the PCjr,
|
||||
it has not been added to the project.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*February 8, 2016*
|
||||
130
_posts/2016-02-17-saving-disks-and-machines.md
Normal file
130
_posts/2016-02-17-saving-disks-and-machines.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
---
|
||||
layout: post
|
||||
title: "Saving Disks and Machines"
|
||||
date: 2016-02-17 14:00:00
|
||||
permalink: /blog/2016/02/17/
|
||||
---
|
||||
|
||||
PCx86 (v1.20.9) now offers new, *much* easier ways to save disks and machines, thanks to the new
|
||||
[Save Disk](/blog/2016/02/17/#saving-disks) and [Save Machine](/blog/2016/02/17/#saving-machines) features.
|
||||
With one click, PCx86 can now generate a single download containing everything you need to embed any of our
|
||||
IBM PC demos on your own web page.
|
||||
|
||||
Saving Disks
|
||||
---
|
||||
|
||||
Floppy disk images can now be saved to your desktop computer by simply clicking the **Save** button next
|
||||
to the floppy disk controls. Select the drive first, and then whatever diskette is shown as being "loaded"
|
||||
in that drive will be saved in your local machine's Downloads folder when you click **Save**.
|
||||
|
||||
If you made any changes to that disk after it was loaded, those changes will be included, so if you want a pristine
|
||||
copy of the disk, click the **Load** button first. PCx86 will ask you to confirm that you really want to reload the
|
||||
disk and discard any changes.
|
||||
|
||||
Note that the disk *should* be downloaded as an **.img** file, which is nothing more than a sector-by-sector binary
|
||||
dump of the disk. For example, a 360Kb double-sided double-density (DSDD) disk contains 9 512-byte sectors in each
|
||||
of the 40 tracks on each of its 2 sides, so when you **Save** a 360Kb disk, the downloaded file should be exactly
|
||||
368,640 bytes large.
|
||||
|
||||
The Mac OS X operating system can automatically mount most **.img** files (from disks created by DOS 2.0 or later).
|
||||
Older DOS 1.x disks, along with most non-DOS disks, do not have a BIOS Parameter Block (BPB) in the boot sector, so
|
||||
most modern operating systems won't recognize the disk format. Other operating systems, like Windows, may require
|
||||
third-party software in order to mount an **.img** file, and some third-party software may prefer a different extension,
|
||||
such as **.ima** or **.bin**.
|
||||
|
||||
It's also recommended that you make your **.img** files *read-only*, so that if you do mount them on your desktop
|
||||
computer, neither you nor the operating system will inadvertently modify the contents of the disk. On OS X, this is
|
||||
easily done with the **chmod** utility.
|
||||
|
||||
For example, if you saved the disk named "PC-DOS 2.00 (Disk 1)", it should have been downloaded as "PCDOS200-DISK1.img"
|
||||
in your Downloads folder, so the OS X Terminal command `chmod -w PCDOS200-DISK1.img` will make it read-only, and
|
||||
`chmod +w PCDOS200-DISK1.img` will make it writable again.
|
||||
|
||||
**NOTE**: Some browsers, notably Safari, do not support named downloads, so any disks you download will end up
|
||||
with default names like "Unknown" or "download". PCx86 will still try to let you know what the original filename was,
|
||||
so that you can rename it appropriately.
|
||||
|
||||
Saving Machines
|
||||
---
|
||||
|
||||
Saving the entire state of any existing IBM PC machine is also much simpler now, using the new **Save Machine** link.
|
||||
You can choose to save a machine in its initial state, or make changes to any of the machine's disks and then save it.
|
||||
All your changes should be preserved.
|
||||
|
||||
Under the bottom-left corner of any IBM PC on the PCjs [website](/), you should now see a
|
||||
[**Save Machine**] link. When you click that link, PCx86 will generate a large chunk of JavaScript containing
|
||||
everything that machine needs to run, including:
|
||||
|
||||
* The machine XML configuration file (eg, "machine.xml")
|
||||
* The machine XSL transformation file (eg, "components.xsl")
|
||||
* The machine CSS stylesheet file (eg, "components.css")
|
||||
* The machine state file (eg, "state.json")
|
||||
* The PCx86 machine emulation script (eg, "pcx86.js")
|
||||
* Copies of all the disk images mounted by the machine
|
||||
|
||||
Let's say you want to save the IBM PC on the PCx86 [home page](/). When you click **Save Machine**, two things should
|
||||
happen:
|
||||
|
||||
* A file will be downloaded (eg, "pcx86.js")
|
||||
* A dialog box will appear with some markup to copy-and-paste
|
||||
|
||||
The dialog box should provide the following information:
|
||||
|
||||
Check your Downloads folder for "pcx86.js", copy it to your web server as "pcx86.js",
|
||||
and then add the following to your web page:
|
||||
|
||||
<div id="ibm5150"></div>
|
||||
...
|
||||
<script type="text/javascript" src="pcx86.js"></script>
|
||||
<script type="text/javascript">embedPC("ibm5150","machine.xml","components.xsl");</script>
|
||||
|
||||
The machine should appear where the <div> is located.
|
||||
|
||||
Copy the downloaded file to your own web server as **pcx86.js**, then create or edit a web page and insert the above text.
|
||||
If **pcx86.js** and your web page are in different folders, then you'll also need to update *src* to include the exact
|
||||
location of the script.
|
||||
|
||||
Some notes:
|
||||
|
||||
* PCx86 may attempt to name the downloaded file **pcx86.json** instead of **pcx86.js**, because a file with a ".js"
|
||||
extension could cause your web browser to block the download.
|
||||
|
||||
* For browsers that don't support named downloads, PCx86 will attempt to open a new window/tab instead. Make sure
|
||||
you copy the *entire* contents of that window into a file named to **pcx86.js** (or **pcx86-dbg.js** if the machine is
|
||||
using the built-in PCx86 debugger).
|
||||
|
||||
* Your browser may also impose size limitations on the download. If nothing happens, the machine data may be too
|
||||
large for your browser; try a different browser (eg, Firefox or Safari) or a different machine.
|
||||
|
||||
* If you have modified any floppy disks mounted by the machine *or* any of the machine's hard disks, those
|
||||
modifications should be saved along with the machine.
|
||||
|
||||
* Any floppy disks mounted during the lifetime of the machine will be added to the machine's state. So, for example,
|
||||
if you want your copy of the machine to include all the Windows 1.01 SDK disks, make sure you have loaded each disk
|
||||
once before clicking **Save Machine**.
|
||||
|
||||
* Any floppy disks *not* mounted during the lifetime of the machine will be *removed* from the machine's list of
|
||||
available disks; we don't want machines running on other websites to be consuming our bandwidth.
|
||||
|
||||
* The machine's current state, including memory and screen contents, are saved as part of the machine state.
|
||||
So, even if the original machine always powers on from scratch, the *copied* machine will always resume at the point
|
||||
it was saved. This behavior, however, can be disabled by passing a *parms* object as the 4th parameter to the
|
||||
*embedPC()* call, overriding the 'state' property:
|
||||
|
||||
```xml
|
||||
<script type="text/javascript">embedPC("ibm5150","machine.xml","components.xsl","{state:null}");</script>
|
||||
```
|
||||
|
||||
While the [PCx86 Documentation](/docs/pcx86/) explains how to create a *new* machine, by writing your own machine
|
||||
XML file and manually copying all the other pieces, the new **Save Machine** feature is the best way to save
|
||||
any *existing* IBM PC and embed it on any other website.
|
||||
|
||||
**WARNING**: While this feature is still "hot of the press," there will probably be some kinks to work out. Every
|
||||
browser seems to have its own idiosyncrasies in terms of what can be downloaded and/or how large it can be. It's
|
||||
also quite possible that certain machine features or modifications may not be properly preserved.
|
||||
|
||||
If you're having trouble with a particular browser or a particular machine, be sure to
|
||||
[let me know](mailto:Jeff@pcjs.org), and then try another browser and/or machine.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*February 17, 2016*
|
||||
95
_posts/2016-02-24-remembering-compaq.md
Normal file
95
_posts/2016-02-24-remembering-compaq.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
layout: post
|
||||
title: Remembering COMPAQ
|
||||
date: 2016-02-24 14:00:00
|
||||
permalink: /blog/2016/02/24/
|
||||
---
|
||||
|
||||
IBM is obviously the company everyone thinks of first when we talk about the IBM PC -- after all, IBM is right
|
||||
there in the name. They designed the thing. So IBM, and in particular the folks who worked in Boca Raton at IBM's
|
||||
Entry Systems Division in the early 1980's, deserve all the credit for defining what eventually became known as the
|
||||
Industry Standard Architecture (ISA) PC platform.
|
||||
|
||||
However, the dawn of the PC era also saw the rise and fall of many other personal computer companies.
|
||||
One of the more exceptional companies from that era was COMPAQ. It would be a mistake to dismiss COMPAQ as just
|
||||
another company that set out to "copy" or "clone" IBM's design, because from the very beginning, COMPAQ's ambitions
|
||||
went beyond mere imitation. They were really in the *compatibility* business.
|
||||
|
||||
Innovation in the PC industry would not have been as rapid and diverse if IBM had been the only supplier of
|
||||
compatible machines and accessories. If IBM had been able to control the market, they probably would have continued
|
||||
some of the monopolistic practices they had perfected in the mainframe era. Market control might have been a win for
|
||||
IBM, but it almost certainly would *not* have been a win for customers.
|
||||
|
||||
In 1982, the founders of COMPAQ took a big gamble, by reportedly investing around a million dollars to create
|
||||
a system that mimicked IBM's without infringing on it. I don't know if COMPAQ pioneered the concept of "clean room"
|
||||
design, where the engineers designing and coding have no direct contact with the hardware and software they're cloning,
|
||||
but they were certainly successful.
|
||||
|
||||
But that wasn't COMPAQ's most important contribution. That was just the price they had to "pay to play." Armed with
|
||||
compatible hardware and software, COMPAQ proceeded to innovate in ways that IBM did not.
|
||||
|
||||
Look at COMPAQ's first product: the COMPAQ Portable. For starters, it was *portable*. And unlike
|
||||
IBM machines, it didn't force you to choose between a higher-quality text-only (MDA) display or a lower-quality
|
||||
graphics-capable (CGA) display. COMPAQ improved on IBM's mutually-exclusive display choices by creating a monochrome
|
||||
display that could display both high-quality text *and* graphics.
|
||||
|
||||
IBM released their own portable unit a year later, but it looked suspiciously COMPAQ-like, and it lacked COMPAQ's
|
||||
innovative display.
|
||||
|
||||
That tradition of innovation at COMPAQ continued for many years. Sometimes they even seemed *more* concerned about
|
||||
compatibility than IBM did. For example, faster machines sometimes broke speed-sensitive floppy-based copy protection
|
||||
schemes, which COMPAQ tried to avoid by automatically *slowing* the machine down whenever a floppy drive was spinning.
|
||||
|
||||
To my knowledge, IBM never bothered with such a feature -- maybe IBM was simply being pragmatic, or perhaps they were
|
||||
just a little arrogant, operating on the theory that if the universe revolves around IBM, then the planets will
|
||||
simply realign *themselves* (translation: software vendors will release new versions if they want to remain IBM-compatible).
|
||||
|
||||
Sadly, COMPAQ was ultimately absorbed by another behemoth -- Hewlett Packard -- and then simply disappeared. Today,
|
||||
all we have are the memories.
|
||||
|
||||
### Speaking of Memories
|
||||
|
||||
In particular, Read-Only Memories: we have precious few of those, too. I finally obtained
|
||||
a [ROM Dump](/devices/pcx86/rom/compaq/portable/) from COMPAQ's first machine, the COMPAQ Portable, but I had to buy
|
||||
a system board on ebay to get it. Considering all the effort (and money) that COMPAQ invested in writing that code,
|
||||
it's a bit depressing that these things haven't been properly preserved and memorialized.
|
||||
|
||||
Looking ahead to the day when PCjs will be able to simulate the original COMPAQ Portable, I thought it would be a good
|
||||
idea to create my own roughly chronological list of [COMPAQ Machines](/devices/pcx86/machine/compaq/) from the 1980s,
|
||||
since they are all machines I would like to see PCjs eventually support:
|
||||
|
||||
- COMPAQ Portable
|
||||
- COMPAQ [Portable] Plus
|
||||
- COMPAQ DeskPro
|
||||
- COMPAQ DeskPro 286
|
||||
- COMPAQ Portable 286
|
||||
- COMPAQ Portable II
|
||||
- [COMPAQ DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/)
|
||||
- COMPAQ Portable III
|
||||
- COMPAQ DeskPro 386/20
|
||||
- COMPAQ Portable 386
|
||||
- COMPAQ DeskPro 386/25
|
||||
- COMPAQ DeskPro 386s
|
||||
- COMPAQ DeskPro 386/20e
|
||||
- COMPAQ SLT Series (SLT/286)
|
||||
- COMPAQ DeskPro 386/33
|
||||
- COMPAQ LTE Series (LTE and LTE/286)
|
||||
|
||||
It's hard to stop at this point, because COMPAQ produced other innovative 80386-based systems, like those in the LTE
|
||||
and LTE Lite series. But I think I need to stick to my original plan and draw a line at the end of the 1980s.
|
||||
|
||||
### Regarding The COMPAQ Name
|
||||
|
||||
As best I can tell, COMPAQ preferred to print its company name in all-caps, so that's my practice as well.
|
||||
|
||||
However, it seems that sometime between the release of [COMPAQ MS-DOS 3.10](/disks/pcx86/dos/compaq/3.10/) and
|
||||
[COMPAQ MS-DOS 3.31](/disks/pcx86/dos/compaq/3.31/), there may have been a shift in policy. Both products still
|
||||
called themselves `The COMPAQ Personal Computer MS-DOS`, but in 3.31, the copyright string changed to
|
||||
`Compaq Computer Corp.`
|
||||
|
||||
Their all-caps practice also extended to product names (eg, `COMPAQ DESKPRO`), at least in their marketing literature.
|
||||
Contemporary news stories, however, tended to lower-case the product name (eg, `COMPAQ Deskpro`). I've decided to
|
||||
split the difference and use mixed-case where it seems appropriate (eg, `COMPAQ DeskPro`).
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*February 24, 2016*
|
||||
48
_posts/2016-03-06-touching-windows.md
Normal file
48
_posts/2016-03-06-touching-windows.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
layout: post
|
||||
title: Touching Windows
|
||||
date: 2016-03-06 09:00:00
|
||||
permalink: /blog/2016/03/06/
|
||||
---
|
||||
|
||||
Yesterday, I fired up [Windows 3.1](/disks/pcx86/windows/3.10/) and played a complete game of
|
||||
[Windows Solitaire](https://en.wikipedia.org/wiki/Microsoft_Solitaire) on my iPad. It was a bit, um, touchy,
|
||||
but it worked.
|
||||
|
||||
{% comment %}[<img src="/blog/images/ipad-solitaire-small.jpg" alt="Windows Solitaire on iPad"/>](/blog/images/ipad-solitaire.jpg){% endcomment %}
|
||||
{% include screenshot.html src="/blog/images/ipad-solitaire-small.jpg" title="iPad running Solitaire on Windows 3.10" link="/blog/images/ipad-solitaire.jpg" %}
|
||||
|
||||
The basic touch events are:
|
||||
|
||||
- `touchstart`
|
||||
- `touchmove`
|
||||
- `touchend`
|
||||
|
||||
which roughly correspond to `mousedown`, `mousemove`, and `mouseup` events, except that your finger isn't
|
||||
exactly like a mouse -- it doesn't have *buttons* for one thing -- so there are several usability problems
|
||||
that must be solved.
|
||||
|
||||
One of the problems is how to deal with the *default behaviors* of touch events. For example, tapping on a
|
||||
PCjs screen is normally how you trigger the iPad's soft keyboard, and dragging your finger across the screen
|
||||
normally scrolls the page up and down. However, you don't want *either* of those behaviors when you're
|
||||
trying to move the machine's mouse pointer around or click on things.
|
||||
|
||||
PCjs attempts to resolve the tapping problem by disabling default behaviors most of the time, except when
|
||||
two taps occur more than 1/2 second apart. This allows a quick *double-tap* to be treated as a double-click,
|
||||
while a pair of slower taps allows the soft keyboard to activate.
|
||||
|
||||
Another problem is how to differentiate between *moving the mouse* (ie, without any buttons pressed) and
|
||||
*dragging the mouse* (ie, with the left button pressed). Newer devices offer the option of relying on "3D Touch"
|
||||
(aka Force Touch), and using pressure to determine the user's intent, but most devices don't have that feature,
|
||||
and I'm not sure I'd want to rely on that anyway.
|
||||
|
||||
The PCjs solution: if you *tap and hold* for more than 200ms, and then start moving, your movements become the
|
||||
equivalent of a *mouse drag* operation.
|
||||
|
||||
This is my first stab at touch-to-mouse conversion, and it's not perfect, but it's already fairly usable,
|
||||
as my Solitaire experiment demonstrates. And there are still some open questions, such as how to intuitively
|
||||
support operations like *right drag*, since the current *mouse drag* operation is inherently *left drag*;
|
||||
one solution would be to rely on multi-touch and use two fingers to trigger right-button operations.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*March 6, 2016*
|
||||
34
_posts/2016-03-12-demos-of-windows386-and-windows-3x.md
Normal file
34
_posts/2016-03-12-demos-of-windows386-and-windows-3x.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
layout: post
|
||||
title: Demos of Windows/386 and Windows 3.x
|
||||
date: 2016-03-12 14:00:00
|
||||
permalink: /blog/2016/03/12/
|
||||
---
|
||||
|
||||
I recently added some more demos to the PCjs Project, to showcase its ability to run old 80286-based and
|
||||
80386-based software, such as [Windows/386](/disks/pcx86/windows/2.0x/), [Windows 3.0](/disks/pcx86/windows/3.00/),
|
||||
[Windows 3.1](/disks/pcx86/windows/3.10/), and [Windows 95](/disks/pcx86/windows/win95/4.00.950/).
|
||||
|
||||
{% include screenshot.html src="/disks/pcx86/windows/2.0x/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows/386 2.01" link="/disks/pcx86/windows/2.0x/" %}
|
||||
{% include screenshot.html src="/disks/pcx86/windows/3.00/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/EGA, Windows 3.00" link="/disks/pcx86/windows/3.00/" %}
|
||||
{% include screenshot.html src="/disks/pcx86/windows/3.10/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/VGA, Windows 3.10" link="/disks/pcx86/windows/3.10/" %}
|
||||
{% include screenshot.html src="/disks/pcx86/windows/win95/4.00.950/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows 95" link="/disks/pcx86/windows/win95/4.00.950/" %}
|
||||
|
||||
As the [OS/2 Museum](http://www.os2museum.com/wp/windows386-2-01/) points out, [Windows/386 2.01](/disks/pcx86/windows/2.0x/)
|
||||
was the first Microsoft product to specifically target the 80386. However, not only was it *not* a 32-bit operating
|
||||
system, it didn't even run Windows applications in protected-mode. Windows apps ran in V86-mode, and even then, *only*
|
||||
if you started Windows by running `WIN386.EXE`.
|
||||
|
||||
There may have been some incidental protection advantages to running Windows in V86-mode instead of real-mode,
|
||||
but the primary advantages were the ability to simulate expanded memory (EMS) using the 80386's paging capabilities,
|
||||
and the ability to run multiple DOS applications simultaneously.
|
||||
|
||||
Alternatively, you could start Windows/386 with `WIN86.COM`, which reverted to the older Windows 1.x memory model,
|
||||
where all Windows applications ran in real-mode and only one DOS application could be run at a time.
|
||||
|
||||
Strangely, unlike all previous and subsequent versions of Windows, there was no `WIN` command in Windows/386.
|
||||
|
||||
At least there was no `LOSE` command.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*March 12, 2016*
|
||||
56
_posts/2016-04-30-the-intel-8080-cpu.md
Normal file
56
_posts/2016-04-30-the-intel-8080-cpu.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
layout: post
|
||||
title: Introducing the Intel 8080 CPU
|
||||
date: 2016-04-30 14:00:00
|
||||
permalink: /blog/2016/04/30/
|
||||
---
|
||||
|
||||
Or rather, introducing [PC8080](/modules/pc8080/), a new 8080-based machine emulator recently added to the
|
||||
PCjs Project.
|
||||
|
||||
Our first [8080 Test Machine](/devices/pc8080/machine/exerciser/) loads a copy of the
|
||||
[8080 Exerciser](https://web.archive.org/web/20151006085348/http://www.idb.me.uk/sunhillow/8080.html)
|
||||
(specifically, [8080EX1](/devices/pc8080/rom/exerciser/8080EX1.MAC)) and intercepts the exerciser's CP/M console
|
||||
calls so that you can see its progress in the Control Panel window. It's a "headless" test machine
|
||||
(no keyboard or display), so that's all you get.
|
||||
|
||||
The good news: PC8080 passes all the 8080 Exerciser tests. And it doesn't do it by using all sorts of weird
|
||||
"flags tables" that most other 8080 emulators seem to fall back on.
|
||||
|
||||
Like all the other CPU emulations in the PCjs Project, PC8080 never "calculates" the flags unless/until they are
|
||||
actually required, which considerably speeds up arithmetic operations.
|
||||
|
||||
Of particular note are the 8080's subtract, compare, and decrement operations, which actually perform addition,
|
||||
not subtraction, by using two's complement arithmetic in "stages": the first stage (inverting the source operand)
|
||||
occurs *before* the addition, and the second stage (incrementing the inverted operand) occurs *after* the addition.
|
||||
And it appears to be the result of the *first* stage, not the second, that determines the state of the Auxiliary
|
||||
Carry flag (AF).
|
||||
|
||||
The behavior of the Auxiliary Carry flag (AF) and the associated DAA instruction are probably the most significant
|
||||
(and least understood) *arithmetic* differences between the 8080 and all later x86-based CPUs. Well, there's also
|
||||
the fact that the 8080 doesn't provide an Overflow flag (OF). Internally however, PC8080 retains the ability to
|
||||
calculate overflow (since PC8080 was a fork of PCjs), which should be useful when we add Z80 support to PC8080.
|
||||
|
||||
On a related note, [Ken Shirriff](http://www.righto.com/) has some fascinating blog posts on the 8085 that also
|
||||
provide clues as to how the 8080 likely operates:
|
||||
|
||||
* [Inside the ALU of the 8085 microprocessor (January 2013)](http://www.righto.com/2013/01/inside-alu-of-8085-microprocessor.html)
|
||||
* [Silicon reverse engineering: The 8085's undocumented flags (February 2013)](http://www.righto.com/2013/02/looking-at-silicon-to-understanding.html)
|
||||
* [The 8085's register file reverse engineered (March 2013)](http://www.righto.com/2013/03/register-file-8085.html)
|
||||
* [Reverse-engineering the 8085's ALU and its hidden registers (July 2013)](http://www.righto.com/2013/07/reverse-engineering-8085s-alu-and-its.html)
|
||||
* [Reverse-engineering the flag circuits in the 8085 processor (July 2013)](http://www.righto.com/2013/07/reverse-engineering-flag-circuits-in.html)
|
||||
* [Reverse-engineering the 8085's decimal adjust circuitry (August 2013)](http://www.righto.com/2013/08/reverse-engineering-8085s-decimal.html)
|
||||
|
||||
The idea is to make [PC8080](/modules/pc8080/) sufficiently configurable so that it will work with a variety of
|
||||
8080-based systems, including those with memory-mapped video displays (like
|
||||
[Space Invaders](/devices/pc8080/machine/invaders/)), as well as simpler terminal-based systems, like the CP/M-based
|
||||
systems of old.
|
||||
|
||||
In fact, as soon as [Space Invaders](/devices/pc8080/machine/invaders/) is working, my next planned adaptation
|
||||
is a DEC VT100 terminal emulator (itself an 8080-based machine) which can then be "wired up" to other PCjs machine
|
||||
simulations. This will not be yet-another VT100-compatible emulation -- which, like 8080 emulators, has been done to
|
||||
death -- but rather a simulation of the original VT100 hardware, building on [Adam Mayer's](https://github.com/phooky)
|
||||
work [reverse-engineering the VT100](https://github.com/phooky/VT100-Hax).
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*April 30, 2016*
|
||||
95
_posts/2016-05-04-the-sharpening.md
Normal file
95
_posts/2016-05-04-the-sharpening.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
layout: post
|
||||
title: The Sharpening
|
||||
date: 2016-05-04 08:00:00
|
||||
permalink: /blog/2016/05/04/
|
||||
---
|
||||
|
||||
This was the week of The Sharpening.
|
||||
|
||||
A while back, I updated most of the machines to use higher-resolution "screens". For example, a typical
|
||||
[EGA video configuration](/devices/pcx86/video/ibm/ega/1984-09-13/128kb-autolockfs.xml) now specifies a *screenWidth*
|
||||
of 1280 and *screenHeight* of 700, dimensions which are exactly twice the standard EGA resolution.
|
||||
|
||||
That change had no effect on the machine's operation, but it did improve the machine's appearance, because
|
||||
most people are using much higher resolution monitors today, so by using a higher-resolution "screen" (canvas),
|
||||
less interpolation is happening when a machine's screen image is scaled up to fill your browser window.
|
||||
|
||||
The amount of scaling *also* depends on whether the machine allows itself to be stretched to fill the browser window.
|
||||
For example, this [machine](/devices/pcx86/machine/5160/ega/640kb/array/machine.xml) (used by the
|
||||
[EGA Machine Array Demo](/devices/pcx86/machine/5160/ega/640kb/array/)) is limited to an overall *width* of 680 pixels,
|
||||
no matter how large you make your browser window:
|
||||
|
||||
```xml
|
||||
<machine id="ibm5160" class="pc" border="1" width="680px" float="left" background="#FAEBD7">
|
||||
```
|
||||
|
||||
but most machines don't specify a (maximum) overall width, so their screen canvas is allowed to stretch far beyond
|
||||
the initial *screenWidth* and *screenHeight*, thanks to some additional CSS settings.
|
||||
|
||||
Larger screens mean less interpolation, which is a good thing if you want the screens to look less "fuzzy."
|
||||
However, interpolation also happens at a deeper level, because internally, PCjs uses two canvases to move pixels
|
||||
from the machine's frame buffer to your browser: the screen canvas, which I've already discussed, and another
|
||||
canvas called the *buffer* canvas, where changes to the machine's frame buffer are, um, buffered.
|
||||
|
||||
The *buffer* canvas has the same dimensions as the machine's frame buffer, whereas the *screen* canvas
|
||||
has generally higher dimensions (as explained above), which your browser may then be stretching to even higher
|
||||
dimensions, depending on your monitor resolution and browser size.
|
||||
|
||||
The differential between the *buffer* canvas and *screen* canvas is where additional interpolation (fuzziness)
|
||||
creeps in. That is where The Sharpening now occurs.
|
||||
|
||||
All the browsers I've tested so far (Chrome, Firefox, and Safari) support a
|
||||
[Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
|
||||
[Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) property named
|
||||
[imageSmoothingEnabled](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled),
|
||||
which eliminates much of the fuzziness that would occur when copying pixels from the lower-resolution *buffer* canvas
|
||||
to the higher-resolution *screen* canvas.
|
||||
|
||||
So I've added a new [Video](/docs/pcx86/video/) property named *smoothing* that can be set to "true" or "false",
|
||||
and I've set it to "false" for most machines in the project. If *smoothing* is not set, your browser continues to
|
||||
use its default interpolation method.
|
||||
|
||||
{% include screenshot.html src="/blog/images/si1978-fuzzier.png" width="339" height="388" title="Space Invaders (Fuzzier)" link="/devices/pc8080/machine/invaders/?smoothing=true" %}
|
||||
{% include screenshot.html src="/blog/images/si1978-sharper.png" width="339" height="388" title="Space Invaders (Sharper)" link="/devices/pc8080/machine/invaders/?smoothing=false" %}
|
||||
|
||||
For some people, this might be a matter of taste, because less fuzziness necessarily means more pixelation (ie, you
|
||||
can see individual pixels more clearly), which becomes more noticeable when switching a machine **Full Screen**.
|
||||
So I've also added a URL *smoothing* parameter you can use to override a machine's default setting; eg:
|
||||
|
||||
http://www.pcjs.org/devices/pc8080/machine/invaders/?smoothing=true
|
||||
|
||||
See for yourself, by clicking on each of the [Space Invaders](/devices/pc8080/machine/invaders/) images above and
|
||||
then clicking the **Full Screen** button; both images link to the same machine, but left one enables image smoothing,
|
||||
while the right one does not.
|
||||
|
||||
Aspect Ratio
|
||||
---
|
||||
|
||||
The *smoothing* property joins another recent [Video](/docs/pcx86/video/) property, *aspect*, that was added in a
|
||||
[release](https://github.com/jeffpar/pcjs/releases/tag/v1.21.5) last month.
|
||||
|
||||
To recap, aspect ratio is display width divided by display height, but the choice of aspect ratio is complicated by
|
||||
the fact that none of the early IBM video card/monitor combinations (with the exception of the VGA) displayed square
|
||||
pixels, and (with the exception of the MDA) they could display text and graphics at a variety of resolutions.
|
||||
|
||||
So, for those users who either 1) don't like the aspect ratios that PCjs has chosen, or 2) just want to squeeze or
|
||||
stretch a machine's screen a bit more, there is now an *aspect* parameter you can append to the URL of any page
|
||||
containing one or more PCjs machines.
|
||||
|
||||
For example:
|
||||
|
||||
http://www.pcjs.org/disks/pcx86/dos/ibm/1.00/?aspect=2.0
|
||||
|
||||
will modify the height of the machine's screen to conform to the requested aspect ratio of 2.0. The screen should still
|
||||
be responsive to any browser resizing while still retaining that aspect ratio.
|
||||
|
||||
---
|
||||
|
||||
That's all for now. Work continues on the new [PC8080](/modules/pc8080/) emulator and the
|
||||
[Space Invaders](/devices/pc8080/machine/invaders/) test machine. More on that later, when it's finished.
|
||||
|
||||
Until then, May the 4th be with you!
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*May 4, 2016*
|
||||
66
_posts/2016-08-03-the-vt100-terminal.md
Normal file
66
_posts/2016-08-03-the-vt100-terminal.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
layout: post
|
||||
title: The VT100 Terminal
|
||||
date: 2016-08-03 18:00:00
|
||||
permalink: /blog/2016/08/03/
|
||||
machines:
|
||||
- type: pc8080
|
||||
id: vt100
|
||||
config: /devices/pc8080/machine/vt100/machine.xml
|
||||
---
|
||||
|
||||
Summer has been filled with distractions, but I've finally begun making headway on a
|
||||
[DEC VT100 Terminal](/devices/pc8080/machine/vt100/) simulation.
|
||||
|
||||
Unlike other VT100 emulators, this isn't simply an emulation of VT100 protocols. It's a simulation of the VT100's
|
||||
8080 CPU, running the original [VT100 Firmware](/devices/pc8080/rom/vt100/) inside the [PC8080](/modules/pc8080/)
|
||||
CPU emulator, along with other essential components that the CPU interacts with to control the display, keyboard, and
|
||||
serial port.
|
||||
|
||||
Most of the VT100's (non-AVO) functionality should be operational now. Once the VT100 "powers on," it should clear
|
||||
the screen and briefly display a "WAIT" message as it processes its Non-Volatile RAM (NVR). When you see a blinking cursor,
|
||||
it's ready to use. Press the SET-UP key to configure the terminal, just as you would an original VT100 terminal.
|
||||
|
||||
For even more fun, check out the [Dual VT100 Terminals](/devices/pc8080/machine/vt100/dual/) demo.
|
||||
|
||||
{% include machine.html id="vt100" %}
|
||||
|
||||
Function keys are mapped as follows:
|
||||
|
||||
- F1: PF1
|
||||
- F2: PF2
|
||||
- F3: PF3
|
||||
- F4: PF4
|
||||
- F6: BREAK
|
||||
- F7: LINE FEED
|
||||
- F8: NO SCROLL
|
||||
- F9: SET-UP
|
||||
|
||||
From the SET-UP screen, you can press **4** to switch to LOCAL mode and verify local operation of most VT100
|
||||
keys. The following keys have special meaning inside SET-UP Mode:
|
||||
|
||||
- 0: RESET
|
||||
- 2: SET/CLEAR TAB
|
||||
- 3: CLEAR ALL TABS
|
||||
- 4: ONLINE/LOCAL
|
||||
- 5: SET-UP A/B
|
||||
- 6: TOGGLE FEATURE
|
||||
- 7: TRANSMIT SPEED
|
||||
- 8: RECEIVE SPEED
|
||||
- 9: 80/132 COLUMNS
|
||||
- SHIFT-S: Save SET-UP Features
|
||||
- SHIFT-R: Restore SET-UP Features
|
||||
|
||||
The initial goal for the simulation is to provide a virtual terminal that can communicate with other PCjs machines
|
||||
and provide a realistic serial communication experience, all from the comfort of your web browser.
|
||||
|
||||
Eventually, I would also like to recreate a fully commented source-code listing for the [DEC VT100 ROMs](/devices/pc8080/rom/vt100/).
|
||||
DEC's [VT100 Technical Manuals](/pubs/dec/vt100/) are excellent, and they provide an enormous amount of hardware detail,
|
||||
but they are a bit light on certain programming details, and there are no firmware listings like those those that IBM provided
|
||||
in the early IBM PC Technical Reference manuals.
|
||||
|
||||
Check out the [VT100 Debugger Configuration](/devices/pc8080/machine/vt100/debugger/) for additional information about
|
||||
VT100 internals and links to other technical resources.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Aug 3, 2016*
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
layout: post
|
||||
title: Connecting an IBM PC to a DEC VT100 Terminal
|
||||
date: 2016-08-19 10:00:00
|
||||
permalink: /blog/2016/08/19/
|
||||
machines:
|
||||
- id: ibm5170
|
||||
type: pcx86
|
||||
connection: com2->vt100.serialPort
|
||||
config: /devices/pcx86/machine/5170/ega/2048kb/rev3/machine.xml
|
||||
- id: vt100
|
||||
type: pc8080
|
||||
connection: serialPort->ibm5170.com2
|
||||
config: /devices/pc8080/machine/vt100/machine.xml
|
||||
---
|
||||
|
||||
Now that you've had a chance to play with a standalone [VT100 Terminal](/devices/pc8080/machine/vt100/), not to mention
|
||||
[Dual VT100 Terminals](/devices/pc8080/machine/vt100/dual/), it's time to take [PCjs Machines](/) to the next level, and
|
||||
begin connecting PCs to terminals.
|
||||
|
||||
Below you'll find an 80286-based [IBM PC AT](/devices/pcx86/machine/5170/ega/2048kb/rev3/) connected to an 8080-based
|
||||
[VT100 Terminal](/devices/pc8080/machine/vt100/) via the PC's **COM2** serial port.
|
||||
|
||||
At this point, the connection is *very* thin. The [PCx86](/modules/pcx86/) [SerialPort](/modules/pcx86/lib/serialport.js)
|
||||
and [PC8080](/modules/pc8080/) [SerialPort](/modules/pc8080/lib/serialport.js) each export exactly two methods:
|
||||
|
||||
- *initConnection()*
|
||||
- *receiveData()*
|
||||
|
||||
The *receiveData()* method always returns *success*, because both SerialPort components buffer all received data.
|
||||
For now, the VT100 SerialPort component avoids overflowing the VT100 firmware's buffers by automatically throttling the flow
|
||||
of data whenever the firmware, in desperation, issues an XOFF. This "Auto XOFF" behavior will eventually be superseded by
|
||||
additional interfaces that provide conventional RS-232 signal-based flow control.
|
||||
|
||||
What can you do with these machines? Click on the PC screen and type:
|
||||
|
||||
DIR > COM2
|
||||
|
||||
or any command redirected to COM2. The output should appear on the VT100's screen.
|
||||
|
||||
A more interesting example:
|
||||
|
||||
CTTY COM2
|
||||
|
||||
redirects all *stdout* (screen) and *stdin* (keyboard) I/O to the VT100. Then click on the VT100 screen to switch focus,
|
||||
and you can now interact with the PC via the terminal. The `CTTY CON` command will restore control to the PC.
|
||||
|
||||
More useful scenarios include machines running PC-based debuggers that communicate via a serial port, such as the
|
||||
OS/2 kernel debugger, Windows debuggers like `WDEB386`, some versions of `SYMDEB`, and so on. PC-to-PC configurations will
|
||||
also be possible, enabling live demonstrations of classic communication software packages such as `CROSSTALK`.
|
||||
|
||||
I've also made it incredibly easy to put machines like this on any PCjs web page. For this blog post, all I had to do
|
||||
was add the following Front Matter to the top of the Markdown file:
|
||||
|
||||
machines:
|
||||
- id: ibm5170
|
||||
type: pcx86
|
||||
connection: com2->vt100.serialPort
|
||||
config: /devices/pcx86/machine/5170/ega/2048kb/rev3/machine.xml
|
||||
- id: vt100
|
||||
type: pc8080
|
||||
connection: serialPort->ibm5170.com2
|
||||
config: /devices/pc8080/machine/vt100/machine.xml
|
||||
|
||||
and then embed the machines in the post, each with a single line:
|
||||
|
||||
{% raw %}
|
||||
{% include machine.html id="ibm5170" %}
|
||||
{% include machine.html id="vt100" %}
|
||||
{% endraw %}
|
||||
|
||||
For people rolling their own web pages, [the basics](/docs/pcx86/) haven't changed, and adding a serial connection merely
|
||||
requires adding a *connection* property (eg, `connection:"com2->vt100.serialPort"`) to the *parms* parameter passed to the
|
||||
*embedPCx86()* interface. A similar property (eg, `connection:"serialPort->ibm5170.com2"`) must also be added to the *parms*
|
||||
passed to *embedPC8080()*.
|
||||
|
||||
For the more adventurous, a [Dual Debugger Configuration](/devices/pcx86/machine/5170/ega/2048kb/rev3/debugger/vt100/)
|
||||
is also available.
|
||||
|
||||
{% include machine.html id="ibm5170" %}
|
||||
|
||||
{% include machine.html id="vt100" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Aug 19, 2016*
|
||||
53
_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md
Normal file
53
_posts/2016-10-06-introducing-pdpjs-a-pdp-11-emulator.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
layout: post
|
||||
title: Introducing PDPjs, a PDP-11 Emulator
|
||||
date: 2016-10-06 15:00:00
|
||||
permalink: /blog/2016/10/06/
|
||||
machines:
|
||||
- id: test1170
|
||||
type: pdp11
|
||||
debugger: true
|
||||
config: /devices/pdp11/machine/1170/vt100/debugger/machine.xml
|
||||
connection: dl11->vt100.serialPort
|
||||
- id: vt100
|
||||
type: pc8080
|
||||
config: /devices/pc8080/machine/vt100/machine.xml
|
||||
connection: serialPort->test1170.dl11
|
||||
---
|
||||
|
||||
[PDPjs](/devices/pdp11/machine/) is the newest addition to the PCjs family of emulators, joining PCx86, PC8080, and C1Pjs.
|
||||
|
||||
While PDPjs may eventually support a range of DEC PDP machines, my current focus is on the PDP-11, starting with the
|
||||
PDP-11/70. From there, I'll work backwards to support other PDP-11 models, such as the PDP-11/45, until I reach the
|
||||
beginning of the PDP-11 line: the PDP-11/20.
|
||||
|
||||
I'm starting with the top-of-the-line PDP-11/70 largely because the core of the emulator is being adapted from the
|
||||
JavaScript [PDP-11/70 Emulator (v1.3)](http://skn.noip.me/pdp11/pdp11.html) written by
|
||||
Paul Nankervis, who has generously given permission to use his code in PCjs. Since his emulator is a fully functional
|
||||
11/70, it made sense to start there and work backwards, factoring out features as needed.
|
||||
|
||||
The code has already undergone a lot of refactoring. Opcodes are now decoded by function tables rather than a single
|
||||
switch statement, and every opcode is implemented with a discrete function. Other refactoring includes flag management,
|
||||
interrupt management, and device management.
|
||||
|
||||
Most of the work remaining is in device management. Like other PCjs emulators, PDPjs has a Bus component,
|
||||
[bus.js](/modules/pdp11/lib/bus.js), that allows separate device components to register I/O handlers for specific
|
||||
UNIBUS addresses. During the initial port, I moved all of Paul's original device management code into one "catch-all"
|
||||
component, [device.js](/modules/pdp11/lib/device.js), which has now been converted to the new I/O registration model.
|
||||
|
||||
The first new device component is [serialport.js](/modules/pdp11/lib/serialport.js), which is currently the
|
||||
only means PDPjs has of communicating with the outside world. So you can try
|
||||
[PDPjs connected to a VT100 Terminal](/devices/pdp11/machine/1170/vt100/), by clicking the "Run" button on the test machine.
|
||||
The test machine is running [custom boot code](/apps/pdp11/boot/test/), adapted from boot code written by Paul, but due to the
|
||||
lack of other device support, nothing can be booted yet.
|
||||
|
||||
Obviously PDPjs is very much a work-in-progress. Before I proceed much farther, I really want to put the CPU through
|
||||
some rigorous testing, so I'll be on the lookout for some comprehensive PDP-11 instruction tests. Or I'll write my own,
|
||||
and compare results across 1 or 2 other PDP-11 emulators.
|
||||
|
||||
{% include machine.html id="test1170" %}
|
||||
|
||||
{% include machine.html id="vt100" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Oct 6, 2016*
|
||||
34
_posts/2016-10-21-booting-pdp-11-basic.md
Normal file
34
_posts/2016-10-21-booting-pdp-11-basic.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
layout: post
|
||||
title: Booting PDP-11 BASIC
|
||||
date: 2016-10-21 23:00:00
|
||||
permalink: /blog/2016/10/21/
|
||||
machines:
|
||||
- id: test1120
|
||||
type: pdp11
|
||||
debugger: true
|
||||
autoMount: ''
|
||||
config: /devices/pdp11/machine/1120/basic/debugger/machine.xml
|
||||
---
|
||||
|
||||
[PDPjs](http://www.pcjs.org/devices/pdp11/machine/) can now simulate a PDP-11/20. It was one of the first PDP-11
|
||||
models, and since it had no MMU, it was limited to a maximum of 56Kb of RAM (or as DEC would say, 28K words), since
|
||||
the top 8Kb (or 4K words) of its 16-bit address space was reserved for UNIBUS devices.
|
||||
|
||||
The first PDPjs test of a PDP-11/20 machine was running [PDP-11 BASIC](/apps/pdp11/tapes/basic/), by loading
|
||||
it directly into memory from the original paper tape image. This required changes to the [RAM](/modules/pdp11/lib/ram.js)
|
||||
component, including a new *loadImage()* interface that understands DEC's paper tape format.
|
||||
|
||||
To provide a more "authentic" experience, there is also a new [PC11 Paper Tape](/devices/pdp11/pc11/) component,
|
||||
and machines can now be configured to include a virtual paper tape reader, with an [assortment of paper tapes](/apps/pdp11/tapes/)
|
||||
ready to be attached.
|
||||
|
||||
In the real world, before you could load *any* paper tape, you had to first enter a
|
||||
[Bootstrap Loader](/apps/pdp11/boot/bootstrap/). PDPjs has simplified that process by including the Bootstrap Loader as
|
||||
just another paper tape image that you can "Load" directly into memory. Once that has been loaded, you can then read any
|
||||
other paper tape image, once you "Attach" it to the paper tape reader.
|
||||
|
||||
{% include machine.html id="test1120" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Oct 21, 2016*
|
||||
69
_posts/2016-11-08-you-should-have-voted-for-the-pdp-11.md
Normal file
69
_posts/2016-11-08-you-should-have-voted-for-the-pdp-11.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
layout: post
|
||||
title: You Should Have Voted For The PDP-11
|
||||
date: 2016-11-08 11:00:00
|
||||
permalink: /blog/2016/11/08/
|
||||
machines:
|
||||
- id: test1170
|
||||
type: pdp11
|
||||
debugger: true
|
||||
config: /devices/pdp11/machine/1170/panel/debugger/machine.xml
|
||||
---
|
||||
|
||||
Greetings from an alternate reality where DEC's elegant PDP-11 architecture beat out Intel's gross 8086 architecture,
|
||||
and DEC managed to gracefully evolve the 16-bit PDP-11 into powerful 32-bit and 64-bit successors, all while maintaining
|
||||
excellent backward compatibility.
|
||||
|
||||
Unfortunately, you're stuck in your reality, so you have no idea what I'm talking about. Basically, your ancestors voted
|
||||
for the cheapest solution rather than the best solution, and now you have to live with the consequences.
|
||||
|
||||
The good news: [PDPjs](/devices/pdp11/machine/1170/panel/debugger/) makes it possible for you to go back in time, and for a moment at least,
|
||||
and relive the PDP-11 experience. It's still a somewhat primitive experience, but PDPjs is a work-in-progress, so hang in
|
||||
there.
|
||||
|
||||
The latest release, v1.30.3, adds the following features:
|
||||
|
||||
- Functional [Front Panels](/devices/pdp11/panel/1170/#front-panel-basics) (check out the demo below)
|
||||
- ROMs such as DEC's [M9312 ROMs](/devices/pdp11/rom/M9312/) can now be installed
|
||||
- Support for DEC's [RL11 Disk Controller](/devices/pdp11/rl11/) has been implemented
|
||||
|
||||
To test RL11 support below, then select the "XXDP+ Diagnostics" disk from the "Disk Drive Controls",
|
||||
click "Load", and wait for the message:
|
||||
|
||||
Mounted disk "XXDP+ Diagnostics" in drive RL0
|
||||
|
||||
Then start the machine (click "Run") and make sure the following prompt has been displayed:
|
||||
|
||||
PDP-11 MONITOR V1.0
|
||||
|
||||
BOOT>
|
||||
|
||||
At the prompt, type "BOOT RL0". The following text should appear:
|
||||
|
||||
CHMDLD0 XXDP+ DL MONITOR
|
||||
BOOTED VIA UNIT 0
|
||||
28K UNIBUS SYSTEM
|
||||
|
||||
ENTER DATE (DD-MMM-YY):
|
||||
|
||||
RESTART ADDR: 152010
|
||||
THIS IS XXDP+. TYPE "H" OR "H/L" FOR HELP.
|
||||
|
||||
.
|
||||
|
||||
And that's the extent of my testing, so if you try anything else and it doesn't work, feel free to
|
||||
[open an issue](https://github.com/jeffpar/pcjs/issues).
|
||||
|
||||
Better yet, fork the [PCjs Project](https://github.com/jeffpar/pcjs), debug the problem yourself, test a fix,
|
||||
and then send me a pull request. :-)
|
||||
|
||||
Finally, another shout-out to Paul Nankervis, who not only generously gave permission
|
||||
to use code from his JavaScript [PDP-11/70 Emulator (v1.4)](http://skn.noip.me/pdp11/pdp11.html), but has also patiently
|
||||
answered all my questions.
|
||||
|
||||
I'm [@jeffpar](http://twitter.com/jeffpar) and I approve this blog post.
|
||||
|
||||
{% include machine.html id="test1170" %}
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Nov 8, 2016*
|
||||
94
_posts/2016-11-13-curious-pdp-11-features.md
Normal file
94
_posts/2016-11-13-curious-pdp-11-features.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
layout: post
|
||||
title: Curious PDP-11 Features
|
||||
date: 2016-11-13 13:00:00
|
||||
permalink: /blog/2016/11/13/
|
||||
---
|
||||
|
||||
Last week, I ran PDPjs through a series of early [PDP-11 Paper Tape Diagnostics](/apps/pdp11/tapes/diags/).
|
||||
These tests were originally released in 1970 and are documented in such DEC publications as the
|
||||
[MAINDEC USER REFERENCE MANUAL](http://archive.pcjs.org/pubs/dec/pdp11/diags/MAINDEC_User_Reference_Manual_Oct73.pdf)
|
||||
(October 1973).
|
||||
|
||||
[Tests 1-12](/apps/pdp11/tapes/diags/#tests-1-12) (MAINDEC-11-D0AA-PB through MAINDEC-11-D0LA-PB)
|
||||
uncovered a couple of uninteresting bugs in the emulator that affected the ASRB and RORB instructions, both involving some
|
||||
incorrect masks. But aside from those hiccups, the tests ran fine. Ditto for [Test 13](/apps/pdp11/tapes/diags/#test-13).
|
||||
|
||||
Things got much more interesting in [Test 14](/apps/pdp11/tapes/diags/#test-14). For starters (and as DEC's documentation
|
||||
points out), the test will immediately fail (HALT) if run on a PDP-11/40 or PDP-11/45, because it tests the RESERVED
|
||||
instruction trap by using the MUL opcode, which was only reserved on early PDP-11's, like the PDP-11/20.
|
||||
|
||||
After switching to a [PDP-11/20 configuration](/devices/pdp11/machine/1120/panel/debugger/), Test 14 uncovered a series of
|
||||
problems, including:
|
||||
|
||||
- The DL11 transmitter must generate an interrupt *immediately* after being enabled
|
||||
- When a RESERVED instruction trap occurs with an invalid stack pointer, the RESERVED instruction trap must be immediately
|
||||
followed by a stack overflow trap
|
||||
- When a hardware interrupt occurs with an invalid stack pointer, the hardware interrupt trap must be immediately followed
|
||||
by a stack overflow trap (ie, before the first instruction of the hardware interrupt handler is executed)
|
||||
- When a BUS error occurs while fetching an opcode, the address of the opcode must be pushed by the trap handler
|
||||
- When a trap handler loads a new PSW that allows a hardware interrupt to be acknowledged, the acknowledgement must be delayed
|
||||
by one instruction
|
||||
|
||||
And finally, the most interesting bug uncovered by Test 14 involved instructions like this:
|
||||
|
||||
MOV R0,(R0)+
|
||||
|
||||
Imagine that R0 contains 1000. PDPjs (as well as several other emulators I tested) would write the value 1000 to address
|
||||
1000 after auto-incrementing R0 to 1002.
|
||||
|
||||
Unfortunately, that's wrong. Apparently, the PDP-11 performs both source and destination address calculations *before*
|
||||
reading and writing the source and destination values. So, in the above example, the value 1002 must be written to address 1000.
|
||||
|
||||
I should add that not all emulators get this wrong: [SimH](https://github.com/simh/simh), the gold standard of PDP-11
|
||||
emulators, handles it correctly.
|
||||
|
||||
Don't confuse this behavior with the order in which the source and destination operands are processed (source operands
|
||||
are always decoded first, destination operands next), or with the fact that the auto-increment mode is actually a *post*-
|
||||
increment mode, whereas auto-decrement is a *pre*-decrement mode. Those things are also true, but they have nothing to do
|
||||
with this bug.
|
||||
|
||||
PDPjs resolved the bug by using a special (negative) value to indicate a register source operand, which is then converted
|
||||
to the register's current value after both operands have been decoded. Test 14 now passes.
|
||||
|
||||
Next up: [Test 15](/apps/pdp11/tapes/diags/#test-15), which uncovered a couple more problems:
|
||||
|
||||
- The DLL transmitter must *not* generate an interrupt if it is immediately *disabled* after being *enabled*
|
||||
- The (old) RTI instruction needs to manage the Trace flag like the (new) RTT instruction if the machine is a PDP-11/20
|
||||
|
||||
Test 15 was written for the earliest PDP-11 models, which didn't have the RTT instruction, so when an RTI instruction
|
||||
enabled the Trace flag, action on the flag was deferred until after the next instruction. Newer models moved that deferred
|
||||
behavior to the RTT instruction, and changed RTI to act on the Trace flag immediately.
|
||||
|
||||
This seems like a regrettable decision. It would have been far better for the new instruction to have the new behavior,
|
||||
and to leave the original RTI instruction alone, for maximum backward compatibility. DEC engineers probably regretted not
|
||||
having separate RTI and RTT instructions from the beginning, and they probably concluded that very little system software
|
||||
would be affected by changing RTI.
|
||||
|
||||
Unfortunately, these diagnostics are one of the casualties of that decision, meaning that this diagnostic will *not* run
|
||||
successfully on an 11/70. And [Test 15](/apps/pdp11/tapes/diags/#test-15) is not alone in that regard.
|
||||
[Test 14](/apps/pdp11/tapes/diags/#test-14) also will not run properly on an 11/70; there again, the problem could have been
|
||||
avoided if DEC had decided to designate an opcode as *permanently* RESERVED, and then used that for all future RESERVED
|
||||
instruction tests, instead of using opcodes that would become new instructions later (eg, MUL).
|
||||
|
||||
The most recent paper tape diagnostic I've run is the [11/70 CPU EXERCISER](/apps/pdp11/tapes/diags/#md-11-1170-cpu-exerciser),
|
||||
and after several more bug fixes, PDPjs passes that test now, too. That's something not even
|
||||
[SimH](https://github.com/simh/simh) is currently able to do (as of November 2016).
|
||||
|
||||
---
|
||||
|
||||
It's worth noting that all these diagnostic paper tape images are in the [Absolute Loader](/apps/pdp11/tapes/absloader/) format,
|
||||
but unlike tapes like [PDP-11 BASIC](/apps/pdp11/tapes/basic/), they don't include a start address, so you have to read the
|
||||
documentation for each test to learn the starting procedure, including any switches that should be set first.
|
||||
|
||||
However, PDPjs makes life a bit simpler for you. As noted for other [DEC PDP-11 Tape Images](/apps/pdp11/tapes/), any
|
||||
"Absolute Format" tape can be loaded directly into RAM using the machine's "Load" button instead of "Attach", allowing you
|
||||
to bypass the usual three-step process of loading the [Bootstrap Loader](/apps/pdp11/boot/bootstrap/) in order to load the
|
||||
[Absolute Loader](/apps/pdp11/tapes/absloader/) in order to load the desired tape.
|
||||
|
||||
Moreover, PDPjs' JSON-encoded paper tape images support an *exec* property that allows the start address to be explicitly
|
||||
set, which will override any start address inside the image. For these diagnostics, I have set the start address (usually 200)
|
||||
via the *exec* property.
|
||||
|
||||
*[@jeffpar](http://twitter.com/jeffpar)*
|
||||
*Nov 13, 2016*
|
||||
Loading…
Reference in a new issue