Skip to content
This repository was archived by the owner on Dec 24, 2020. It is now read-only.

Commit

Permalink
Some improvements for FOOTBALL, including support for the 386 LOADALL…
Browse files Browse the repository at this point in the history
… instruction
  • Loading branch information
jeffpar committed Jan 29, 2016
1 parent 84b6dbe commit 3d2bc42
Show file tree
Hide file tree
Showing 27 changed files with 4,431 additions and 3,981 deletions.
39 changes: 39 additions & 0 deletions _posts/2016-01-23-early-os2-artifacts.md
Original file line number Diff line number Diff line change
@@ -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/pc/os2/misc/football/87058/#football-design-document).

To shed some light on those efforts, I recently added a few [OS/2 Prototype Disks](/disks/pc/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/pc/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*
22 changes: 0 additions & 22 deletions _posts/2016-01-23-unearthing-os2.md

This file was deleted.

4 changes: 2 additions & 2 deletions devices/pc/machine/compaq/deskpro386/ega/2048kb/machine.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
<rom id="romEGA" addr="0xc0000" size="0x4000" file="/devices/pc/video/ibm/ega/ibm-ega.json" notify="videoEGA"/>
<rom id="romBIOS" addr="0xf8000" size="0x8000" alias="[0xf0000,0xffff0000,0xffff8000]" file="/devices/pc/bios/compaq/deskpro386/1988-01-28/1988-01-28.json"/>
<video ref="/devices/pc/video/ibm/ega/ibm-ega-128kb-autolockfs.xml"/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal-functions.xml"/>
<debugger id="debugger" messages="fault" commands=""/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal-sysreq.xml"/>
<debugger id="debugger" messages="" commands=""/>
<panel ref="/devices/pc/panel/wide386.xml"/>
<fdc ref="/disks/pc/library.xml" automount='{A: {name: "PC-DOS 3.20 (Disk 1)", path: "/disks/pc/dos/ibm/3.20/PCDOS320-DISK1.json"}, B: {name: "PC-DOS 3.20 (Disk 2)", path: "/disks/pc/dos/ibm/3.20/PCDOS320-DISK2.json"}}'/>
<hdcNone id="hdcAT" type="at" drives='[{name:"20Mb Hard Disk",type:2}]'/>
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Instruction documentation is also available:
Our [Publication Archive](/pubs/) includes these PC-related resources:

* [Datasheets](/pubs/pc/datasheets/)
* [Intel CPU Documents](/pubs/pc/reference/intel/)
* [Magazines](/pubs/pc/magazines/)
* [Programming Guides](/pubs/pc/programming/) (eg, [OS/2](/pubs/pc/programming/os2/))
* [Reference Manuals](/pubs/pc/reference/)
Expand Down
2,030 changes: 1,016 additions & 1,014 deletions docs/pcjs/demos/pc-dbg.js

Large diffs are not rendered by default.

1,775 changes: 888 additions & 887 deletions docs/pcjs/demos/pc.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/x86/ops/aaa.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ it is a BCD digit.

The following example shows how to add BCD numbers then adjust the result:

MOV AH,0 ;Clear AH for most significant digit
MOV AL,6 ;BCD 6 in AL
ADD AL,5 ;Add BCD 5 to digit in AL
AAA ;AH=1, AL=1 representing BCD 11.
MOV AH,0 ; Clear AH for most significant digit
MOV AL,6 ; BCD 6 in AL
ADD AL,5 ; Add BCD 5 to digit in AL
AAA ; AH=1, AL=1 representing BCD 11.

### Algorithm

Expand Down
8 changes: 8 additions & 0 deletions modules/pcjs/lib/debugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -4793,6 +4793,14 @@ if (DEBUGGER) {

if (typeCPU == null) typeCPU = type >> Debugger.TYPE_CPU_SHIFT;

if (iIns == Debugger.INS.LOADALL) {
if (typeCPU == Debugger.CPU_80286) {
sOperands = "[%800]";
} else if (typeCPU == Debugger.CPU_80386) {
sOperands = "ES:[" + (dbgAddr.fAddr32? 'E':'') + "DI]";
}
}

var typeSize = type & Debugger.TYPE_SIZE;
if (typeSize == Debugger.TYPE_NONE) {
continue;
Expand Down
2 changes: 1 addition & 1 deletion modules/pcjs/lib/x86.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ var X86 = {
SEL: {
RPL: 0x0003, // requested privilege level (0-3)
LDT: 0x0004, // table indicator (0: GDT, 1: LDT)
MASK: 0xFFF8 // table index
MASK: 0xFFF8 // table offset
},
DESC: { // Descriptor Table Entry
LIMIT: { // LIMIT bits 0-15 (or OFFSET if this is an INTERRUPT or TRAP gate)
Expand Down
31 changes: 25 additions & 6 deletions modules/pcjs/lib/x86cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -1123,8 +1123,10 @@ X86CPU.prototype.resetRegs = function()
* More recently, opCS was added to selectively snapshot an instruction's original CS in case an
* exception occurs accessing the stack after a new CS has been loaded, allowing the exception handler
* to recover the old CS and make instructions like CALLF restartable; otherwise, opCS should remain -1.
*
* Ditto for opSS and the SS register.
*/
this.opCS = -1;
this.opCS = this.opSS = -1;
this.opLIP = this.opLSP = X86.ADDR_INVALID;

/*
Expand Down Expand Up @@ -2131,7 +2133,13 @@ X86CPU.prototype.setLIP = function(addr)
{
this.regLIP = addr|0;
this.regLIPLimit = (this.segCS.base + this.segCS.limit)|0;

/*
* TODO: Verify the proper source for CPL. Should it come from segCS.cpl or segCS.dpl?
* Also, note that LOADALL386 wants it to come from segSS.dpl.
*/
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient

if (I386) this.resetSizes();
/*
* Here, we need to additionally test whether the prefetch buffer (adwPrefetch) has been allocated yet,
Expand Down Expand Up @@ -2983,7 +2991,7 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
* probeAddr(addr, size, fLinear)
*
* Used by the Debugger to probe addresses without risk of triggering a page fault, and by internal
* functions, like fnFaultMessage(), that must also avoid triggering faults, since they're not part of
* functions, like fnCheckFault(), that must also avoid triggering faults, since they're not part of
* standard CPU operation.
*
* Since originally written, I've also relaxed the requirement that the request be contained entirely
Expand Down Expand Up @@ -3767,10 +3775,21 @@ X86CPU.prototype.popWord = function()
/**
* pushData(d, width, size)
*
* This function serves two very limited purposes: 1) the ability to push data according to a previous
* operand size (width), and 2) the ability to write fewer bytes than the width if necessary (size).
*
* The former occurs when a 32-bit code segment performs a 16:32 call to a 16-bit code segment; after the
* new 16-bit code segment is loaded (and possible stack switch occurs), the return address (both segment
* and offset) must still be pushed as 32-bit values.
*
* The latter occurs with segment register pushes. When a 32-bit operand size is in effect (ie, width is 4),
* only the low 16 bits should be written (size must be 2). For all other kinds of pushes, width and size are
* impliedly the same.
*
* @this {X86CPU}
* @param {number} d is the data to push at current SP; SP decreased by size
* @param {number} width is the width of the data to push, in bytes (must be either 2 or 4)
* @param {number} size is the size of the data to push, in bytes (must be > 0 and <= width)
* @param {number} size is the size of the data to push, in bytes (must be 1, 2, or 4, and <= width)
*/
X86CPU.prototype.pushData = function(d, width, size)
{
Expand Down Expand Up @@ -3800,13 +3819,13 @@ X86CPU.prototype.pushData = function(d, width, size)
switch(size) {
case 1:
this.setByte(regLSP, d);
break
break;
case 2:
this.setShort(regLSP, d);
break
break;
case 4:
this.setLong(regLSP, d);
break
break;
default:
this.assert(false);
break;
Expand Down
42 changes: 26 additions & 16 deletions modules/pcjs/lib/x86func.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,12 @@ X86.fnCALLw = function(dst, src)
X86.fnCALLF = function(off, sel)
{
/*
* Since we always push the return address AFTER calling setCSIP(), and since either push could trigger
* Since we always push the return address AFTER calling setCSIP(), and since either push could trigger a
* fault (eg, segment fault, page fault, etc), we must not only snapshot regLSP into opLSP, but also the
* current CS into opCS, so that fnFault() can always make CALLF restartable.
* current CS into opCS, so that fnFault() can always make CALLF restartable. Ditto for opSS and the SS register.
*/
this.opCS = this.getCS();
this.opSS = this.getSS();
this.opLSP = this.regLSP;
var oldIP = this.getIP();
var oldSize = (I386? this.sizeData : 2);
Expand All @@ -572,7 +573,7 @@ X86.fnCALLF = function(off, sel)
this.pushData(oldIP, oldSize, oldSize);
}
this.opLSP = X86.ADDR_INVALID;
this.opCS = -1;
this.opCS = this.opSS = -1;
};

/**
Expand Down Expand Up @@ -3942,25 +3943,32 @@ X86.fnFault = function(nFault, nError, nCycles, fHalt)
* Prior to each new burst of instructions, stepCPU() sets fComplete to true, and the only (normal) way
* for fComplete to become false is through stopCPU(), which isn't ordinarily called, except by the Debugger.
*/
this.resetSizes();
this.setIP(this.opLIP - this.segCS.base);
this.setLIP(this.opLIP);
}
else if (this.model >= X86.MODEL_80186) {

fDispatch = true;

if (this.nFault < 0) {
/*
* Single-fault (error code is passed through, and the responsible instruction is restartable;
* the call to resetSizes() is critical, otherwise setIP() may update IP with the wrong size if
* the current instruction contains an OPERAND size override).
* Single-fault (error code is passed through, and the responsible instruction is restartable.
*/
this.resetSizes();
if (this.opCS != -1) {
/*
* HACK: We must slam 3 into this.segCS.cpl to ensure that loading the original CS segment doesn't
* fail. For example, if we faulted in the middle of a ring transition that loaded CS with a higher
* privilege (lower CPL) code segment, then our attempt here to reload the lower privilege (higher CPL)
* code segment could be viewed as a privilege violation (which it would be outside this context).
*/
this.segCS.cpl = 3;
this.setCS(this.opCS);
this.opCS = -1;
}
this.setIP(this.opLIP - this.segCS.base);
this.setLIP(this.opLIP);
if (this.opSS != -1) {
this.setSS(this.opSS);
this.opSS = -1;
}
if (this.opLSP !== X86.ADDR_INVALID) {
this.setSP((this.regESP & ~this.segSS.maskAddr) | (this.opLSP - this.segSS.base));
this.opLSP = X86.ADDR_INVALID;
Expand All @@ -3970,22 +3978,24 @@ X86.fnFault = function(nFault, nError, nCycles, fHalt)
/*
* Double-fault (error code is always zero, and the responsible instruction is not restartable)
*/
nError = 0; nFault = X86.EXCEPTION.DF_FAULT;
nError = 0;
nFault = X86.EXCEPTION.DF_FAULT;
}
else {
/*
* Triple-fault (usually referred to in Intel literature as a "shutdown", but at least on the 80286,
* it's actually a "reset")
*/
nFault = -1; nError = 0;
nError = 0;
nFault = -1;
this.resetRegs();
fDispatch = fHalt = false;
}
}

if (X86.fnFaultMessage.call(this, nFault, nError, fHalt)) {
if (X86.fnCheckFault.call(this, nFault, nError, fHalt)) {
/*
* If this is a fault that would normally be dispatched BUT fnFaultMessage() wants us to halt,
* If this is a fault that would normally be dispatched BUT fnCheckFault() wants us to halt,
* then we throw a bogus fault number (-1), simply to interrupt the current instruction in exactly
* the same way that a dispatched fault would interrupt it.
*/
Expand Down Expand Up @@ -4060,7 +4070,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
};

/**
* fnFaultMessage(nFault, nError, fHalt)
* fnCheckFault(nFault, nError, fHalt)
*
* Aside from giving the Debugger an opportunity to report every fault, this also gives us the ability to
* halt exception processing in tracks: return true to prevent the fault handler from being dispatched.
Expand All @@ -4076,7 +4086,7 @@ X86.fnPageFault = function(addr, fPresent, fWrite)
* @param {boolean} [fHalt] (true to halt the CPU, false to not, undefined if "it depends")
* @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it
*/
X86.fnFaultMessage = function(nFault, nError, fHalt)
X86.fnCheckFault = function(nFault, nError, fHalt)
{
var bitsMessage = Messages.FAULT;

Expand Down
Loading

0 comments on commit 3d2bc42

Please sign in to comment.