Exploring PCx86, an IBM PC emulator written in JavaScript
I came across PCjs, a JavaScript-based emulator that supports many different vintage computer architectures and spent last week setting up my first web-based PC XT and PC AT emulators with it. Starting with the examples available on PCjs github repository, I copied the required XML, CSS, XSL and JS files to my project and used the following HTML code :
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>pcjs.org | /apps/pcx86/examples/example1.html</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="components.css"> <script type="text/javascript" src="pcx86.js"></script> </head> <body style="font-family: Helvetica, Arial, sans-serif;"> <div id="pcxt"></div> <script type="text/javascript"> embedPCx86("pcxt", "pcxt.xml", "components.xsl"); </script> </body> </html>
Make sure the name of the div matches the value passed to embedPCx86, otherwise there will just be a blank page. Here pcxt.xml specifies the settings for RAM, BIOS, floppy drive and other settings:
<?xml version="1.0" encoding="UTF-8"?> <machine id="example1" type="pcx86" width="720px"> <computer id="pc" name="IBM PC"/> <cpu id="cpu8088" model="8088" autostart="true"/> <ram id="ramLow" addr="0x00000" size="0x10000"/> <rom id="romBASIC" addr="0xf6000" size="0x8000" file="ibm-basic-1.00.json"/> <rom id="romBIOS" addr="0xfe000" size="0x2000" file="1981-04-24.json"/> <keyboard id="keyboard"/> <video id="videoMDA" screenWidth="720" screenHeight="350" fontROM="ibm-mda-cga.json"> <menu> <title>Monochrome Display</title> </menu> </video> <chipset id="chipset" model="5150" sw1="01000001" sw2="11110000"/> </machine>
It is important to ensure that all files mentioned in the XML (BIOS image, floppy disk image, etc.) as well as PCx86 supporting files (available from versions/pcx86 folder) are present in the correct folder, otherwise there will be cryptic error messages. In particular, if some CSS/XSL files are missing, the JS code responsible for initializing the HTML canvas to display the emulator will fail, and the code will report a very misleading error: “Missing <canvas> support. Please try a newer web browser.”
As PCjs only accepts BIOS/disk image in JSON format, it is important to convert the binary images first. You can refer to this and this on how to perform the conversion. Additionally, refer to PCjs online examples here for the necessary configuration entries to emulate different types of machines.
With the correct configuration, I was able to quickly launch the PC XT emulator in my browser:
Serial mouse emulation also works properly with CuteMouse, although the browser text cursor is also shown together with the DOS cursor. This can probably be fixed with CSS styles. Regardless, the cursor is not a big issue for me as I tend to ignore the browser cursor after a while.
One thing I noticed is that sound sometimes doesn’t play in Google Chrome despite the emulator working perfectly otherwise. The reason for this can be found from a message in Chrome console:
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
It seems that audio playback will be disallowed by Chrome unless the user explicitly interacts with the emulator before the audio playback. In the case of the PC XT emulator, you will need to click the emulator before the POST beep for audio to be played subsequently. To fix the issue, open chrome://flags and set “Autoplay policy” to “No user gesture is required”:
Alternatively just use Firefox, which worked during my tests. As for Internet Explorer, I couldn’t get audio playback to work, even with the latest version.
A challenge is to save the current state of the virtual machine, especially the modified hard disk image, so that we can restore the same state even after the browser is restarted. Although this is normally simple for other emulators such as DosBox since disk images are updated directly, PCjs works on a JSON array of the image content and emulator state can only be saved by invoking the “Save Machine” menu option. Unfortunately, this did not work due to a bug in PCjs that uses hard-coded path to the pcx86.js script. As a workaround, I use CSS to hide the default “Save Machine” link and implement my own menu:
<a href="#" onclick='savePC("pcxt", "pcx86.js")'>Save Machine</a>
Here pcxt is the name of the emulator and pcx86.js is the path to the pcx86.js script. With this workaround, machine state can be saved under Firefox. A Javascript file that describes the current state of the emulator will be downloaded and PCjs will also show you the code snippet that can be used to reload the saved state.
Unfortunately, there is no way to simply extract the modified hard disk image without saving the state of the entire machine. The savePC() function will also fail in Chrome with an immediate “Download Failed – Network Error” failure. In Internet Explorer and Firefox, the feature works properly.
When using with CGA display, I also noticed that PCjs seems to ignore the screenWidth and screenHeight attribute of the video tag, and always shows the emulator using 100% screen width. If this is an issue, use the following Javascript code to set the emulator to your preferred width:
setTimeout(function() { document.getElementById('ibm5160').setAttribute("style", "width:50%"); }, 500);
The setTimeOut function is needed to introduce a small delay; otherwise our style changes will simply be reverted by PCjs initialization code.
For those who are interested, I have prepared a PCx86 instance that emulates an BM PC AT 80286 @ 8MHz with 640 KB RAM, dual 1.44MB floppy drives and CGA display. The two floppy disks, accessible as A: and B:, contain MS-DOS 6.22 together with CheckIt, Turbo Pascal 5.5, Norton Commander and many games. Feel free to try it out! (Hint: depending on your screen resolution, you might want to resize the browser window so that the entire emulator screen is visible)