Merge branch 'master' into gh-pages
This commit is contained in:
commit
7d58e02def
289 changed files with 14226 additions and 304 deletions
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ;-))
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 + "')");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue