Merge branch 'master' into gh-pages

This commit is contained in:
Jeff Parsons 2017-01-24 15:38:04 -08:00
commit 7d58e02def
289 changed files with 14226 additions and 304 deletions

View file

@ -49,7 +49,7 @@ if (NODE) {
* with a switch statement).
*
* Eventually, every opcode should end up either in an opXXX() function or opUndefined(). For
* opcodes that perform a simple read and/or write operation, the entire operation is handled by
* opcodes that perform a simple read or write operation, the entire operation is handled by
* the opXXX() function. For opcodes that perform a more extensive read/modify/write operation
* (also known as an update operation), those opXXX() functions usually rely on a corresponding
* fnXXX() helper function.
@ -1455,13 +1455,11 @@ PDP11.opMUL = function(opCode)
{
var src = this.readDstWord(opCode);
var reg = (opCode >> 6) & 7;
if (src & 0x8000) src |= ~0xffff;
var dst = this.regsGen[reg];
if (dst & 0x8000) dst |= ~0xffff;
var result = ~~(src * dst);
var result = ((src << 16) >> 16) * ((dst << 16) >> 16);
this.regsGen[reg] = (result >> 16) & 0xffff;
this.regsGen[reg | 1] = result & 0xffff;
this.updateMulFlags(result);
this.updateMulFlags(result|0);
this.nStepCycles -= (22 + 1);
};

View file

@ -1128,8 +1128,10 @@ class DebuggerPDP11 extends Debugger {
{
if (!this.checkCPU()) return false;
var sCmd = "";
if (fRegs === null) {
fRegs = (!this.sCmdTracePrev || this.sCmdTracePrev == "tr");
sCmd = fRegs? "tr" : "t";
}
this.nCycles = 0;
@ -1178,7 +1180,7 @@ class DebuggerPDP11 extends Debugger {
this.cmp.updateDisplays(-1);
}
this.updateStatus(fRegs || false);
this.updateStatus(fRegs || false, sCmd);
return (this.nCycles > 0);
}
@ -1194,17 +1196,22 @@ class DebuggerPDP11 extends Debugger {
}
/**
* updateStatus(fRegs)
* updateStatus(fRegs, sCmd)
*
* @this {DebuggerPDP11}
* @param {boolean} [fRegs] (default is true)
* @param {string} [sCmd]
*/
updateStatus(fRegs)
updateStatus(fRegs, sCmd)
{
if (!this.fInit) return;
if (fRegs === undefined) fRegs = true;
if (sCmd) {
this.println(DebuggerPDP11.PROMPT + sCmd);
}
var trapStatus = this.cpu.getTrapStatus();
if (trapStatus) {
var reason = trapStatus >> 8;
@ -1218,9 +1225,9 @@ class DebuggerPDP11 extends Debugger {
* if inactive, 1 if stepping over an instruction without a register dump, or 2
* if stepping over an instruction with a register dump.
*/
if (!fRegs || this.nStep == 1)
if (!fRegs || this.nStep == 1) {
this.doUnassemble();
else {
} else {
this.doRegisters();
}
}
@ -3065,6 +3072,7 @@ class DebuggerPDP11 extends Debugger {
this.println("warning: " + Str.toHex(vNew) + " exceeds " + size + "-byte value");
}
this.println("changing " + this.toStrAddr(dbgAddr) + (this.messageEnabled(MessagesPDP11.BUS)? "" : (" from " + this.toStrBase(fnGet.call(this, dbgAddr), size))) + " to " + this.toStrBase(vNew, size));
//noinspection JSUnresolvedFunction
fnSet.call(this, dbgAddr, vNew, size);
}
}
@ -3901,8 +3909,7 @@ class DebuggerPDP11 extends Debugger {
sCmd = "";
}
else if (!fQuiet) {
var sPrompt = ">> ";
this.println(sPrompt + sCmd);
this.println(DebuggerPDP11.PROMPT + sCmd);
}
var ch = sCmd.charAt(0);
@ -4382,6 +4389,8 @@ if (DEBUGGER) {
DebuggerPDP11.HISTORY_LIMIT = DEBUG? 100000 : 1000;
DebuggerPDP11.PROMPT = ">> ";
/*
* Initialize every Debugger module on the page (as IF there's ever going to be more than one ;-))
*/

View file

@ -39,6 +39,31 @@ if (NODE) {
var DiskPDP11 = require("./disk");
}
/**
* Since the Closure Compiler treats ES6 classes as @struct rather than @dict by default,
* it deters us from defining named properties on our components; eg:
*
* this['exports'] = {...}
*
* results in an error:
*
* Cannot do '[]' access on a struct
*
* So, in order to define 'exports', we must override the @struct assumption by annotating
* the class as @unrestricted (or @dict). Note that this must be done both here and in the
* Component class, because otherwise the Compiler won't allow us to *reference* the named
* property either.
*
* TODO: Consider marking ALL our classes unrestricted, because otherwise it forces us to
* define every single property the class uses in its constructor, which results in a fair
* bit of redundant initialization, since many properties aren't (and don't need to be) fully
* initialized until the appropriate init(), reset(), restore(), etc. function is called.
*
* The upside, however, may be that since the structure of the class is completely defined by
* the constructor, JavaScript engines may be able to optimize and run more efficiently.
*
* @unrestricted
*/
class RL11 extends Component {
/**
* RL11(parms)
@ -83,6 +108,10 @@ class RL11 extends Component {
* Define all the properties to be initialized by initController()
*/
this.regRLCS = this.regRLBA = this.regRLDA = this.tmpRLDA = this.regRLMP = this.regRLBE = 0;
this['exports'] = {
'selectDrive': this.selectDrive
};
}
/**
@ -756,15 +785,20 @@ class RL11 extends Component {
/**
* displayDisk(iDrive, fUpdateDrive)
*
* This ensures that the selected disk matches the drive's sDiskPath property, and if fUpdateDrive is set,
* it also ensures that the selected drive matches the specified drive number.
*
* @this {RL11}
* @param {number} iDrive (unvalidated)
* @param {number} iDrive
* @param {boolean} [fUpdateDrive] is true to update the drive list to match the specified drive (eg, the auto-mount case)
* @return {boolean} true if successful, false if not
*/
displayDisk(iDrive, fUpdateDrive)
{
/*
* First things first: validate iDrive.
*/
var fSuccess = false;
if (iDrive >= 0 && iDrive < this.aDrives.length) {
var drive = this.aDrives[iDrive];
var controlDisks = this.bindings["listDisks"];
@ -774,9 +808,24 @@ class RL11 extends Component {
*/
if (controlDisks && controlDrives && controlDisks.options && controlDrives.options) {
/*
* Next, make sure the drive whose disk we're updating is the currently selected drive.
* Next, update the drive if the caller has requested it.
*/
var i;
if (fUpdateDrive) {
this.assert(iDrive == drive.iDrive);
for (i = 0; i < controlDrives.options.length; i++) {
if (Str.parseInt(controlDrives.options[i].value, 10) == drive.iDrive) {
if (controlDrives.selectedIndex != i) {
controlDrives.selectedIndex = i;
}
fSuccess = true;
break;
}
}
}
/*
* Next, make sure the drive whose disk we're updating is the currently selected drive.
*/
var iDriveSelected = Str.parseInt(controlDrives.value, 10);
var sTargetPath = (drive.fLocal? RL11.SOURCE.LOCAL : drive.sDiskPath);
if (!isNaN(iDriveSelected) && iDriveSelected == iDrive) {
@ -785,23 +834,41 @@ class RL11 extends Component {
if (controlDisks.selectedIndex != i) {
controlDisks.selectedIndex = i;
}
fSuccess = true;
break;
}
}
if (i == controlDisks.options.length) controlDisks.selectedIndex = 0;
}
if (fUpdateDrive) {
for (i = 0; i < controlDrives.options.length; i++) {
if (Str.parseInt(controlDrives.options[i].value, 10) == drive.iDrive) {
if (controlDrives.selectedIndex != i) {
controlDrives.selectedIndex = i;
}
break;
}
}
}
return fSuccess;
}
/**
* selectDrive(sDrive)
*
* Used to programmatically select a drive by name.
*
* @this {RL11}
* @param {number} sDrive
* @return {boolean} true if successful, false if not
*/
selectDrive(sDrive)
{
var controlDrives = this.bindings["listDrives"];
if (controlDrives && controlDrives.options) {
var nDrives = controlDrives.options.length;
for (var i = 0; i < nDrives; i++) {
if (controlDrives.options[i].innerHTML == sDrive) {
var iDrive = Str.parseInt(controlDrives.options[i].value, 10);
if (iDrive >= 0) {
return this.displayDisk(iDrive, true);
}
}
}
}
return false;
}
/**

View file

@ -69,33 +69,36 @@ var DiskAPI = {
};
/*
* Common (supported) diskette formats
* Common (supported) disk formats
*
* For no particular reason that I can recall, each entry in DISK_FORMATS is an array of values in "CHS" order:
* Each entry in DISK_FORMATS begins with an array of three "CHS" values:
*
* [# cylinders, # heads, # sectors/track, # bytes/sector, media type]
*
* If the 4th value is omitted, the sector size is assumed to be 512. The order of these "geometric" values mirrors
* the structure of our JSON-encoded disk images, which consist of an array of cylinders, each of which is an array of
* heads, each of which is an array of sector objects.
* The 4th value is optional; if omitted, the sector size is assumed to be 512 bytes. The order of these geometric
* values mirrors the structure of our JSON-encoded disk images, which consist of an array of cylinders, each of which
* is an array of heads, each of which is an array of sector objects.
*
* The 5th value is also optional and is used only with PC-DOS diskettes, to help us verify that the logical format
* matches the physical format; it should be one of the values in DiskAPI.FAT. TODO: Actually use the DiskAPI.FAT values.
*/
DiskAPI.DISK_FORMATS = {
163840: [40,1,8,,0xFE], // media type 0xFE: 40 cylinders, 1 head (single-sided), 8 sectors/track, ( 320 total sectors x 512 bytes/sector == 163840)
184320: [40,1,9,,0xFC], // media type 0xFC: 40 cylinders, 1 head (single-sided), 9 sectors/track, ( 360 total sectors x 512 bytes/sector == 184320)
327680: [40,2,8,,0xFF], // media type 0xFF: 40 cylinders, 2 heads (double-sided), 8 sectors/track, ( 640 total sectors x 512 bytes/sector == 327680)
368640: [40,2,9,,0xFD], // media type 0xFD: 40 cylinders, 2 heads (double-sided), 9 sectors/track, ( 720 total sectors x 512 bytes/sector == 368640)
737280: [80,2,9,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 9 sectors/track, (1440 total sectors x 512 bytes/sector == 737280)
1228800: [80,2,15,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 15 sectors/track, (2400 total sectors x 512 bytes/sector == 1228800)
1474560: [80,2,18,,0xF0], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 18 sectors/track, (2880 total sectors x 512 bytes/sector == 1474560)
2949120: [80,2,36,,0xF0], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 36 sectors/track, (5760 total sectors x 512 bytes/sector == 2949120)
163840: [ 40,1, 8,,0xFE], // media type 0xFE: 40 cylinders, 1 head (single-sided), 8 sectors/track, ( 320 total sectors x 512 bytes/sector == 163840)
184320: [ 40,1, 9,,0xFC], // media type 0xFC: 40 cylinders, 1 head (single-sided), 9 sectors/track, ( 360 total sectors x 512 bytes/sector == 184320)
327680: [ 40,2, 8,,0xFF], // media type 0xFF: 40 cylinders, 2 heads (double-sided), 8 sectors/track, ( 640 total sectors x 512 bytes/sector == 327680)
368640: [ 40,2, 9,,0xFD], // media type 0xFD: 40 cylinders, 2 heads (double-sided), 9 sectors/track, ( 720 total sectors x 512 bytes/sector == 368640)
737280: [ 80,2, 9,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 9 sectors/track, (1440 total sectors x 512 bytes/sector == 737280)
1228800: [ 80,2,15,,0xF9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 15 sectors/track, (2400 total sectors x 512 bytes/sector == 1228800)
1474560: [ 80,2,18,,0xF0], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 18 sectors/track, (2880 total sectors x 512 bytes/sector == 1474560)
2949120: [ 80,2,36,,0xF0], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 36 sectors/track, (5760 total sectors x 512 bytes/sector == 2949120)
/*
* The following are common early hard drive sizes, which we explicitly map to CHS values, since the BPB can mislead us when attempting to calculate total cylinders
* The following are common early hard drive sizes, which we explicitly map to CHS values, since the BPB can mislead us when attempting to calculate total cylinders.
*/
21368320:[615,4,17], // PC AT 20Mb hard drive (type 2)
/*
* Assorted DEC disk pack formats.
*/
2494464: [203,2,12,512], // RK03 single-platter disk cartridge: 203 tracks, 2 heads, 12 sectors/track, 512 bytes/sector, for a total of 2494464 bytes
2494464: [203,2,12,512], // RK03 single-platter disk cartridge: 203 tracks, 2 heads, 12 sectors/track, 512 bytes/sector, for a total of 2494464 bytes
5242880: [256,2,40,256], // RL01K single-platter disk cartridge: 256 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 5242880 bytes
10485760:[512,2,40,256] // RL02K single-platter disk cartridge: 512 tracks, 2 heads, 40 sectors/track, 256 bytes/sector, for a total of 10485760 bytes
};

View file

@ -88,7 +88,7 @@ function addStickyMachine(idMachine, sPosition)
}
/**
* commandMachine(idMachine, sType, sCommand, sValue)
* commandMachine(idMachine, sComponent, sCommand, sValue)
*
* Note that this script is not compiled into any of the machines, since "sticky machines" are feature of the PCjs
* website rather than of the machines. And since the machines are compiled, all their code and data is completely
@ -98,32 +98,34 @@ function addStickyMachine(idMachine, sPosition)
*
* findMachineComponent()
*
* which uses existing Component methods to find the requested component type for a specific machine, and if a
* which uses existing Component methods to find the requested component for a specific machine, and if the
* component is found, then its 'exports' table is checked for an entry matching the specified command string, and if
* an entry is found, then the corresponding function is called with the specified data.
*
* @param {string} idMachine
* @param {string} sType
* @param {string} sComponent
* @param {string} sCommand
* @param {string} [sValue]
*/
function commandMachine(idMachine, sType, sCommand, sValue)
function commandMachine(idMachine, sComponent, sCommand, sValue)
{
if (window.findMachineComponent) {
var component = window.findMachineComponent(idMachine, sType);
var component = window.findMachineComponent(idMachine, sComponent);
if (component) {
var exports = component['exports'];
if (exports) {
var fnCommand = exports[sCommand];
if (fnCommand) {
//noinspection JSUnresolvedFunction
fnCommand.call(component, sValue);
if (!fnCommand.call(component, sValue)) {
window.alert("Error calling " + idMachine + "." + sComponent + "." + sCommand + "(" + sValue + ")");
}
return;
}
}
}
}
console.log("unimplemented: commandMachine('" + idMachine + "','" + typeComponent + "','" + sCommand + "','" + sValue + "')");
console.log("unimplemented: commandMachine('" + idMachine + "','" + sComponent + "','" + sCommand + "','" + sValue + "')");
}
/**