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