In this post, you will see how to play Alley Cat from your web browser.
Last week, I explored gabonator’s Visual C++ and JavaScript port of Alley Cat, one of my favourite DOS games. The project, despite being apparently abandoned since 2014, is still exciting to me as I love the idea of being able to play classic MS-DOS games the modern way without having to start any emulator. The idea also reminds me of SDLPoP, another similar and still active project which develops an SDL version of Prince of Persia using mostly knowledge gained from the disassembly process.
In a nutshell, the author used The Interactive Disassembler (IDA) to generate the ASM code for the original Alley Cat executable and then used another tool to convert the assembly code to its C equivalent. Although the output is in C syntax, it is still cryptic as it still contains register-like variables and methods with hexadecimal names:
However, this is where things become interesting. Instead of spending time understanding the logic and rewriting the code in a more readable (and maintainable manner), the author implements a miniature 8086 emulator in C which can be used to run the converted code. Assembly instructions such as push
and pop
are also written as C methods:
With this approach, there is no need to modify the converted code, except perhaps to fix places where the converter did not do a good job, mostly obscure jumps that cannot be automatically translated into C code. Graphics calls are then modified to update a centralized CGA frame buffer, declared as a bitmap
, to be drawn on screen when necessary:
CFrameBuffer()
{
HDC hDC;
BITMAPINFO bitmapinfo;
hDC=CreateCompatibleDC(NULL);
bitmapinfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
bitmapinfo.bmiHeader.biWidth=Width;
bitmapinfo.bmiHeader.biHeight=-Height;
bitmapinfo.bmiHeader.biPlanes=1;
bitmapinfo.bmiHeader.biBitCount=32;
bitmapinfo.bmiHeader.biCompression=BI_RGB;
bitmapinfo.bmiHeader.biSizeImage=0;
bitmapinfo.bmiHeader.biClrUsed=256;
bitmapinfo.bmiHeader.biClrImportant=256;
m_pBuffer = NULL;
m_ourbitmap=CreateDIBSection(hDC,&bitmapinfo,DIB_RGB_COLORS,&m_pBuffer,0,0);
m_pDC=CreateCompatibleDC(NULL);
m_Old=SelectObject(m_pDC,m_ourbitmap);
DeleteDC(hDC);
FillMemory(m_pBuffer, sizeof(DWORD)*Width*Height, 0xb0);
}
PC speaker functions are also converted to Visual C’s Beep()
function. The ported C code also loads the original .EXE into memory to simulate how an .EXE file is loaded on MS-DOS. Using a similar approach, the code is then converted to JavaScript to be executed on a browser.
Unfortunately, although I tried hard to replicate the author’s progress, the provided C code is missing several important pieces. Several functions are present in the JavaScript port, but not in the C codes, which indicates that the uploads were most likely incomplete. The JavaScript port still works to show the intro screen (see this for a demo) but hangs afterwards since its development was never completed.
Although I do not have the time to continue with the project, I was impressed by the efforts spent in porting the intro screen to JavaScript. After all, the converted C functions are cryptic and do not have meaningful names. I however do not think that this is an efficient approach since the final C codes are simply assembly instructions converted to C and not maintainable. I decided to go with PCjs, a JavaScript based PC emulator, and build a PC XT image that can boot Alley Cat in a browser, ready to play in just a few seconds.
This can be done with just the following HTML code:
<body style="font-family: Helvetica, Arial, sans-serif; display: none">
<div id="ibm5160" style="width: 50%; height: 50%;"></div>
<script type="text/javascript">
embedPCx86('ibm5160','machine.xml','components.xsl','{autoMount:{}}');
setTimeout(function() {
}, 500);
setTimeout(function() {
document.title = "Alley Cat in a web browser";
document.body.style.display = "block";
}, 5000);
</script>
<!--
</body>
And the following machine configuration file:
="1.0"="UTF-8"
="text/xsl"="machine.xsl"
<machine id="ibm5160" type="pcx86"
border="1" pos="center" background="default">
<name pos="center">Alley Cat in a web browser</name>
<computer id="xt-cga-640k" name="IBM PC XT"/>
<ram id="ramLow" addr="0x00000" test="false" size="0xa0000"
comment="0xa0000 (640Kb) size overrides SW1|ROM BIOS
memory test has been disabled"/>
<rom id="romBIOS" addr="0xfe000"
size="0x2000" file="XTBIOS-REV1.json"/>
<video ref="ibm-cga-keygrid.xml"/>
<cpu id="cpu8088" model="8088"
pos="left" padLeft="8px" padBottom="8px">
</cpu>
<fdc id="fdcNEC" autoMount='{A: {name: "AlleyCat",
path: "alleycat.json"}}' pos="left">
</fdc>
<keyboard ref="us83-softkeys.xml"/>
<chipset id="chipset" model="5160" sw1="01001001"/>
<serial id="com1" adapter="1"/>
<serial id="com2" adapter="2"/>
<mouse serial="com2"/>
</machine>
Where alleycat.json is the bootable version of AlleyCat
, converted to JSON using PCjs instructions. To hide the PC XT boot process, the HTML body is hidden at load and only made visible by JavaScript codes after a few seconds, after the machine has finished loading. With this, Alley Cat can load nicely in the browser after just a few seconds and the entire game is playable:
The same approach can be used to make other DOS games playable in the browser without much effort. For those you are interested, the above setup can be accessed via this link. The ported C++ and JavaScript codes can be downloaded here.
See Also