Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-11-07 17:33:09 -08:00
commit 505a429d5b
350 changed files with 21692 additions and 2847 deletions

View file

@ -414,10 +414,11 @@ DiskDump.PCJS_OEM = "PCJS.ORG";
* The BPBs that buildImage() currently supports; these BPBs should be in order of smallest to largest capacity,
* to help ensure we don't select a disk format larger than necessary.
*
* TODO: For now, the code that chooses a default BPB is starting with #1 instead of #0, because Windows 95 (at least
* TODO: For now, the code that chooses a default BPB is starting with #3 instead of #0, because Windows 95 (at least
* when running under VMware) fails to read the contents of such disks correctly. Whether that's my fault or Windows 95's
* fault is still TBD (although it's probably mine -- perhaps 160Kb diskettes aren't supposed to have BPBs?) The simple
* work-around is to avoid creating 160Kb diskette images.
* work-around is to avoid creating 160Kb diskette images (and, to play it safe, I skip 180Kb and 320Kb as well, since
* 360Kb was the most commonly used format after DOS 2.0 introduced it).
*/
DiskDump.aDefaultBPBs = [
[ // define BPB for 160Kb diskette
@ -433,7 +434,39 @@ DiskDump.aDefaultBPBs = [
0xFE, // 0x15: media type (eg, 0xFF: 320Kb, 0xFE: 160Kb, 0xFD: 360Kb, 0xFC: 180Kb)
0x01, 0x00, // 0x16: sectors per FAT (1)
0x08, 0x00, // 0x18: sectors per track (8)
0x01, 0x00, // 0x1A: number of heads (2)
0x01, 0x00, // 0x1A: number of heads (1)
0x00, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media)
],
[ // define BPB for 180Kb diskette
0xEB, 0xFE, 0x90, // 0x00: JMP instruction, following by 8-byte OEM signature
0x50, 0x43, 0x4A, 0x53, 0x2E, 0x4F, 0x52, 0x47, // PCJS_OEM
// 0x49, 0x42, 0x4D, 0x20, 0x20, 0x31, 0x2E, 0x30, // "IBM 1.0" (this is a fake OEM signature)
0x00, 0x02, // 0x0B: bytes per sector (0x200 or 512)
0x01, // 0x0D: sectors per cluster (1)
0x01, 0x00, // 0x0E: reserved sectors; ie, # sectors preceding the first FAT--usually just the boot sector (1)
0x02, // 0x10: FAT copies (2)
0x40, 0x00, // 0x11: root directory entries (0x40 or 64) 0x40 * 0x20 = 0x800 (1 sector is 0x200 bytes, total of 4 sectors)
0x68, 0x01, // 0x13: number of sectors (0x168 or 360)
0xFC, // 0x15: media type (eg, 0xFF: 320Kb, 0xFE: 160Kb, 0xFD: 360Kb, 0xFC: 180Kb)
0x02, 0x00, // 0x16: sectors per FAT (2)
0x09, 0x00, // 0x18: sectors per track (9)
0x01, 0x00, // 0x1A: number of heads (1)
0x00, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media)
],
[ // define BPB for 320Kb diskette
0xEB, 0xFE, 0x90, // 0x00: JMP instruction, following by 8-byte OEM signature
0x50, 0x43, 0x4A, 0x53, 0x2E, 0x4F, 0x52, 0x47, // PCJS_OEM
// 0x49, 0x42, 0x4D, 0x20, 0x20, 0x32, 0x2E, 0x30, // "IBM 2.0" (this is a real OEM signature)
0x00, 0x02, // 0x0B: bytes per sector (0x200 or 512)
0x02, // 0x0D: sectors per cluster (2)
0x01, 0x00, // 0x0E: reserved sectors; ie, # sectors preceding the first FAT--usually just the boot sector (1)
0x02, // 0x10: FAT copies (2)
0x70, 0x00, // 0x11: root directory entries (0x70 or 112) 0x70 * 0x20 = 0xE00 (1 sector is 0x200 bytes, total of 7 sectors)
0x80, 0x02, // 0x13: number of sectors (0x280 or 640)
0xFF, // 0x15: media type (eg, 0xFF: 320Kb, 0xFE: 160Kb, 0xFD: 360Kb, 0xFC: 180Kb)
0x01, 0x00, // 0x16: sectors per FAT (1)
0x08, 0x00, // 0x18: sectors per track (8)
0x02, 0x00, // 0x1A: number of heads (2)
0x00, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media)
],
[ // define BPB for 360Kb diskette
@ -1032,7 +1065,7 @@ DiskDump.logWarning = function(s)
{
var sWarning = "";
if (s) {
sWarning = "diskdump warning: " + s;
sWarning = "DiskDump warning: " + s;
DiskDump.logConsole(sWarning);
}
return sWarning;
@ -1911,8 +1944,7 @@ DiskDump.prototype.buildFATEntry = function(abFAT, iFAT, v)
else {
if (abFAT[iByte] === undefined) abFAT[iByte] = 0;
abFAT[iByte] = (abFAT[iByte] & 0x0F) | ((v & 0xF) << 4);
iByte++;
abFAT[iByte] = (v >> 4);
abFAT[iByte + 1] = (v >> 4);
}
};
@ -2300,7 +2332,7 @@ DiskDump.prototype.buildImageFromFiles = function(aFiles, done)
* Find or build a BPB with enough capacity, and at the same time, calculate all
* the other values we'll need, including total number of data sectors (cDataSectors).
*/
for (var iBPB = 1; iBPB < DiskDump.aDefaultBPBs.length; iBPB++) {
for (var iBPB = 3; iBPB < DiskDump.aDefaultBPBs.length; iBPB++) {
/*
* If this BPB is for a hard drive but a disk size was not specified, skip it.
*/
@ -2369,6 +2401,9 @@ DiskDump.prototype.buildImageFromFiles = function(aFiles, done)
/*
* Build the FAT, noting the starting cluster number that each file will use along the way.
*
* Also, notice that the first byte of the FAT is the "media type" byte that's replicated in the
* BPB at offset 0x15. For old BPB-less diskettes, this is where you must look for the media type.
*/
var abFAT = [];
this.buildFATEntry(abFAT, 0, abBoot[0x15] | 0xF00);
@ -2457,6 +2492,7 @@ DiskDump.prototype.convertToJSON = function()
var aTracks = []; // track array (used only for disk images with track tables)
var iTrack, cbTrack, offTrack, bufTrack, bufSector;
var cbSector = 512; // default sector size
var bMediaType = 0;
var offBootSector = 0;
var cbDiskData = this.bufDisk.length;
@ -2484,6 +2520,7 @@ DiskDump.prototype.convertToJSON = function()
}
var bByte0 = this.bufDisk.readUInt8(offBootSector + DiskAPI.BOOT.JMP_OPCODE);
var bByte1 = this.bufDisk.readUInt8(offBootSector + DiskAPI.BOOT.JMP_OPCODE + 1);
var cbSectorBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.SECTOR_BYTES);
/*
@ -2499,63 +2536,136 @@ DiskDump.prototype.convertToJSON = function()
* image whose logical format doesn't agree with its physical structure.
*/
var fXDFOutput = false;
var disketteFormat = DiskAPI.DISKETTE_FORMATS[cbDiskData];
if (disketteFormat) {
nCylinders = disketteFormat[0];
nHeads = disketteFormat[1];
nSectorsPerTrack = disketteFormat[2];
var diskFormat = DiskAPI.DISK_FORMATS[cbDiskData];
if (diskFormat) {
nCylinders = diskFormat[0];
nHeads = diskFormat[1];
nSectorsPerTrack = diskFormat[2];
cbSector = diskFormat[3] || cbSector;
bMediaType = diskFormat[4] || bMediaType;
}
else {
/*
* See if the first sector of the image contains a valid DOS BPB. That begs the question: what IS a valid
* DOS BPB? For starters, the first word (at offset 0x0B) is invariably 0x0200, indicating a 512-byte sector
* size. I also check the first byte for an Intel JMP opcode (0xEB is JMP with a 1-byte displacement, and
* 0xE9 is JMP with a 2-byte displacement). What else?
*/
if ((bByte0 == X86.OPCODE.JMP || bByte0 == X86.OPCODE.JMPS) && cbSectorBPB == cbSector) {
var nHeadsBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.TOTAL_HEADS);
/*
* I used to do these BPB tests only if diskFormat was undefined, but now I always do them, because I
* want to make sure they're in agreement (and if not, then figure out why not).
*
* See if the first sector of the image contains a valid DOS BPB. That begs the question: what IS a valid
* DOS BPB? For starters, the first word (at offset 0x0B) is invariably 0x0200, indicating a 512-byte sector
* size. I also check the first byte for an Intel JMP opcode (0xEB is JMP with a 1-byte displacement, and
* 0xE9 is JMP with a 2-byte displacement). What else?
*/
var fBPBExists = false;
if ((bByte0 == X86.OPCODE.JMP || bByte0 == X86.OPCODE.JMPS) && cbSectorBPB == cbSector) {
var nHeadsBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.TOTAL_HEADS);
var nSectorsPerTrackBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.TRACK_SECS);
if (nHeadsBPB && nSectorsPerTrackBPB) {
fBPBExists = true;
var bMediaTypeBPB = this.bufDisk.readUInt8(offBootSector + DiskAPI.BPB.MEDIA_TYPE);
var nSectorsTotalBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.TOTAL_SECS);
var nSectorsPerTrackBPB = this.bufDisk.readUInt16LE(offBootSector + DiskAPI.BPB.TRACK_SECS);
if (nSectorsPerTrackBPB && nHeadsBPB) {
var nSectorsPerCylinderBPB = nSectorsPerTrackBPB * nHeadsBPB;
var nCylindersBPB = Math.floor(nSectorsTotalBPB / nSectorsPerCylinderBPB);
var nSectorsPerCylinderBPB = nSectorsPerTrackBPB * nHeadsBPB;
nHeads = nHeadsBPB;
nCylinders = Math.floor(nSectorsTotalBPB / nSectorsPerCylinderBPB);
nSectorsPerTrack = nSectorsPerTrackBPB;
/*
* OK, great, the disk appears to contain a valid BPB. But so do XDF disk images, which are
* diskette images with tracks containing:
*
* 1 8Kb sector (equivalent of 16 512-byte sectors)
* 1 2Kb sector (equivalent of 4 512-byte sectors)
* 1 1Kb sector (equivalent of 2 512-byte sectors)
* 1 512-byte sector (equivalent of, um, 1 512-byte sector)
*
* for a total of the equivalent of 23 512-byte sectors, or 11776 (0x2E00) bytes per track.
* For an 80-track diskette with 2 sides, that works out to a total of 3680 512-byte sectors,
* or 1884160 bytes, or 1.84Mb, which is the exact size of the (only) XDF diskette images we
* currently (try to) support.
*
* Moreover, the first two tracks (ie, the first cylinder) contain only 19 sectors each,
* rather than 23, but XDF disk images still pads those tracks with 4 unused sectors.
*
* So, data for the first track contains 1 boot sector ending at 512 (0x200), 11 FAT sectors
* ending at 6144 (0x1800), and 7 "micro-disk" sectors ending at 9728 (0x2600). Then there's
* 4 (useless?) sectors that end at 11776 (0x2E00).
*
* Data for the second track contains 7 root directory sectors ending at 15360 (0x3C00), followed
* by disk data.
*
* For more details, check out this helpful article: http://www.os2museum.com/wp/the-xdf-diskette-format/
*/
if (nSectorsTotalBPB == 3680 && this.fXDFSupport) {
DiskDump.logWarning("XDF diskette detected, experimental XDF output enabled");
fXDFOutput = true;
if (diskFormat) {
if (nCylinders != nCylindersBPB) {
DiskDump.logWarning("BPB cylinders (" + nCylindersBPB + ") do not match actual cylinders: " + nCylinders);
}
if (nHeads != nHeadsBPB) {
DiskDump.logWarning("BPB heads (" + nHeadsBPB + ") do not match actual heads: " + nHeads);
}
if (nSectorsPerTrack != nSectorsPerTrackBPB) {
DiskDump.logWarning("BPB sectors/track (" + nSectorsPerTrackBPB + ") do not match actual sectors/track: " + nSectorsPerTrack);
}
if (bMediaType && bMediaType != bMediaTypeBPB) {
DiskDump.logWarning("BPB media type (" + bMediaTypeBPB + ") do not match actual media type: " + bMediaType);
}
}
else {
nCylinders = nCylindersBPB;
nHeads = nHeadsBPB;
nSectorsPerTrack = nSectorsPerTrackBPB;
bMediaType = bMediaTypeBPB;
}
/*
* OK, great, the disk appears to contain a valid BPB. But so do XDF disk images, which are
* diskette images with tracks containing:
*
* 1 8Kb sector (equivalent of 16 512-byte sectors)
* 1 2Kb sector (equivalent of 4 512-byte sectors)
* 1 1Kb sector (equivalent of 2 512-byte sectors)
* 1 512-byte sector (equivalent of, um, 1 512-byte sector)
*
* for a total of the equivalent of 23 512-byte sectors, or 11776 (0x2E00) bytes per track.
* For an 80-track diskette with 2 sides, that works out to a total of 3680 512-byte sectors,
* or 1884160 bytes, or 1.84Mb, which is the exact size of the (only) XDF diskette images we
* currently (try to) support.
*
* Moreover, the first two tracks (ie, the first cylinder) contain only 19 sectors each,
* rather than 23, but XDF disk images still pads those tracks with 4 unused sectors.
*
* So, data for the first track contains 1 boot sector ending at 512 (0x200), 11 FAT sectors
* ending at 6144 (0x1800), and 7 "micro-disk" sectors ending at 9728 (0x2600). Then there's
* 4 (useless?) sectors that end at 11776 (0x2E00).
*
* Data for the second track contains 7 root directory sectors ending at 15360 (0x3C00), followed
* by disk data.
*
* For more details, check out this helpful article: http://www.os2museum.com/wp/the-xdf-diskette-format/
*/
if (nSectorsTotalBPB == 3680 && this.fXDFSupport) {
DiskDump.logWarning("XDF diskette detected, experimental XDF output enabled");
fXDFOutput = true;
}
}
}
/*
* Let's see if we can find a corresponding BPB in our table of default BPBs.
*/
var i, iBPB = -1;
if (bMediaType) {
for (i = 0; i < DiskDump.aDefaultBPBs.length; i++) {
if (DiskDump.aDefaultBPBs[i][DiskAPI.BPB.MEDIA_TYPE] == bMediaType) {
iBPB = i;
break;
}
}
}
if (iBPB >= 0) {
if (fBPBExists) {
for (i = DiskAPI.BPB.SECTOR_BYTES; i < DiskAPI.BPB.LARGE_SECS; i++) {
var bDefault = DiskDump.aDefaultBPBs[iBPB][i];
var bActual = this.bufDisk.readUInt8(offBootSector + i);
if (bDefault != bActual) {
DiskDump.logWarning("BPB byte " + str.toHexByte(i) + " default (" + str.toHexByte(bDefault) + ") does not match actual byte: " + str.toHexByte(bActual));
}
}
}
else if (bByte0 == X86.OPCODE.JMPS && bByte1 >= 0x22) {
/*
* I'm going to stick my neck out here and slam a BPB into this disk image, since it doesn't appear
* to have one, which should make it more "mountable" on modern operating systems.
*/
for (i = DiskAPI.BPB.SECTOR_BYTES; i < DiskAPI.BPB.LARGE_SECS+4; i++) {
this.bufDisk.writeUInt8(DiskDump.aDefaultBPBs[iBPB][i] || 0, offBootSector + i);
}
}
else if (bByte0 == 0xF6 && bByte1 == 0xF6) {
/*
* WARNING: I've added this "0xF6" hack expressly to fix boot sectors that may have been zapped by an
* inadvertent reformat, or...?
*/
DiskDump.logWarning("repairing damaged boot sector with BPB for media type " + str.toHexByte(bMediaType));
for (i = 0; i < DiskAPI.BPB.LARGE_SECS+4; i++) {
this.bufDisk.writeUInt8(DiskDump.aDefaultBPBs[iBPB][i] || 0, offBootSector + i);
}
}
else {
DiskDump.logWarning("unrecognized boot sector: " + str.toHexByte(bByte0) + "," + str.toHexByte(bByte1));
}
}
@ -2711,6 +2821,14 @@ DiskDump.prototype.convertToJSON = function()
bufSector = bufTrack.slice(offSector, offSector + cbSectorThisTrack);
if (bMediaType && !iCylinder && !iHead && iSector == 2) {
var bFATType = bufSector.readUInt8(0);
if (bMediaType != bFATType) {
DiskDump.logWarning("wrong media type (" + str.toHexByte(bFATType) + ") in FAT, expected " + str.toHexByte(bMediaType));
}
bMediaType = 0;
}
if (this.fJSONNative) {
sector['sector'] = nSector;
sector['length'] = cbSectorThisTrack;

View file

@ -252,7 +252,7 @@ FileDump.prototype.loadFile = function(sFile, iStart, nSkip, done)
var encoding = null;
var sExt = str.getExtension(sFile);
if (sExt == DumpAPI.FORMAT.JSON || sExt == DumpAPI.FORMAT.HEX || sExt == "lst") {
if (sExt == DumpAPI.FORMAT.JSON || sExt == DumpAPI.FORMAT.HEX || sExt == "lst" || sExt == "txt") {
encoding = "utf8";
}
var options = {encoding: encoding};
@ -343,7 +343,7 @@ FileDump.prototype.setData = function(buf, iStart, nSkip, sExt)
var b, i, j, s;
if (typeof buf == "string") {
var ab = [];
if (sExt == "lst") {
if (sExt == "lst" || sExt == "txt") {
ab = this.parseListing(buf);
}
else if (buf.indexOf('{') >= 0) {
@ -488,7 +488,7 @@ FileDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offDump, nWidth
* correct load (and exec) addresses. For now, we're simply inferring that the first address parsed
* in parseListing() is both the load and exec address.
*/
var sAddr = str.toHexWord(this.addrLoad) + (nBase == 8? " /*" + str.toOct(this.addrLoad, 6) + "*/" : "");
var sAddr = str.toHexWord(this.addrLoad) + (nBase == 8? "/*" + str.toOct(this.addrLoad, 6) + "*/" : "");
sDump += this.dumpLine(2, '"load":' + sAddr + ',"exec":' + sAddr + ',');
}
sDump += this.dumpLine(2, (sKey? '"' + sKey + '":' : "") + this.sJSONWhitespace + chOpen);
@ -520,7 +520,7 @@ FileDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offDump, nWidth
if (cbItem > 2) {
sLine += v;
} else {
sLine += str.toHexWord(v) + (nBase == 8? " /*" + str.toOct(v & 0xffff, 6) + "*/" : "");
sLine += str.toHexWord(v) + (nBase == 8? "/*" + str.toOct(v & 0xffff, 6) + "*/" : "");
}
}
else {

View file

@ -3461,7 +3461,10 @@ ChipSet.prototype.advanceDMA = function(channel, fInit)
}
if (!channel.masked) {
chipset.bus.setByte(addrCur, b);
if (BACKTRACK) {
/*
* WARNING: Do NOT assume that obj is valid; if the sector data was not found, there will be no obj.
*/
if (BACKTRACK && obj) {
if (!off && obj.file) {
if (chipset.messageEnabled(Messages.DISK)) {
chipset.printMessage("loading " + obj.file.sPath + '[' + obj.offFile + "] at %" + str.toHex(addrCur), true);

View file

@ -804,7 +804,7 @@ var SectorInfo;
* for every disk loaded BEFORE the initBus() phase; any disk loaded AFTER that point will get its Debugger
* reference, if any, from the disk controller passed to the Disk() constructor.
*
* @this {ChipSet}
* @this {Disk}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
@ -858,7 +858,7 @@ Disk.prototype.powerUp = function(data, fRepower) {
*
* This is a callback issued by the Disk component once the load() from powerUp() has finished.
*
* @this {HDC}
* @this {Disk}
* @param {Object} drive
* @param {Disk} disk is set if the disk was successfully mounted, null if not
* @param {string} sDiskName
@ -1106,13 +1106,13 @@ Disk.prototype.build = function(buffer, fModified)
{
var disk;
var cbDiskData = buffer? buffer.byteLength : 0;
var disketteFormat = DiskAPI.DISKETTE_FORMATS[cbDiskData];
var diskFormat = DiskAPI.DISK_FORMATS[cbDiskData];
if (disketteFormat) {
this.nCylinders = disketteFormat[0];
this.nHeads = disketteFormat[1];
this.nSectors = disketteFormat[2];
this.cbSector = 512;
if (diskFormat) {
this.nCylinders = diskFormat[0];
this.nHeads = diskFormat[1];
this.nSectors = diskFormat[2];
this.cbSector = (diskFormat[3] || 512);
var cdw = this.cbSector >> 2, dwPattern = 0, dwChecksum = 0;
var ib = 0;
@ -1138,7 +1138,7 @@ Disk.prototype.build = function(buffer, fModified)
this.dwChecksum = dwChecksum;
disk = this;
} else {
this.notice("Unrecognized diskette format (" + cbDiskData + " bytes)");
this.notice("Unrecognized disk format (" + cbDiskData + " bytes)");
}
if (this.fnNotify) {
@ -1481,6 +1481,8 @@ Disk.prototype.buildFileTable = function()
else if (cbDisk == 320 * 1024 && this.getClusterEntry(dir, 0, 0) == DiskAPI.FAT.MEDIA_320KB) {
dir.lbaTotal = 640;
dir.nEntries = 112;
this.assert(this.nHeads == 2);
dir.nClusterSecs++; // 320Kb disks use 2 sectors/cluster
fValid = true;
}
else {

View file

@ -470,7 +470,14 @@ FDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
}
});
for (i = 0; i < aOptions.length; i++) {
control.options[i] = aOptions[i];
try {
/*
* TODO: Determine why this line blows up in IE8; are the properties of an options object not settable in IE8?
*/
control.options[i] = aOptions[i];
} catch(e) {
break;
}
}
}
@ -547,7 +554,7 @@ FDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
control.onclick = function onClickSaveDrive(event) {
var controlDrives = fdc.bindings["listDrives"];
if (controlDrives && controlDrives.options && fdc.aDrives) {
var iDriveSelected = str.parseInt(controlDrives.value, 10);
var iDriveSelected = str.parseInt(controlDrives.value, 10) || 0;
var drive = fdc.aDrives[iDriveSelected];
if (drive) {
/*
@ -1069,17 +1076,17 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
* so all we have to do is mount a blank diskette and let disk.restore() do the rest; ie, there's nothing to
* "load" (it's a purely synchronous operation).
*
* Otherwise, we must call loadDiskette(); in the common case, loadDiskette() will have already "auto-mounted"
* Otherwise, we must call loadDrive(); in the common case, loadDrive() will have already "auto-mounted"
* the diskette, so it will return true, and then we restore any deltas to the current image.
*
* However, if loadDiskette() returns false, then it has initiated the load for a *different* disk image,
* However, if loadDrive() returns false, then it has initiated the load for a *different* disk image,
* so we must mark ourselves as "not ready" again, and add another "wait for ready" test in Computer before
* finally powering the CPU.
*/
if (fLocal) {
this.mountDiskette(iDrive, sDisketteName, sDiskettePath);
this.mountDrive(iDrive, sDisketteName, sDiskettePath);
}
else if (this.loadDiskette(iDrive, sDisketteName, sDiskettePath, true)) {
else if (this.loadDrive(iDrive, sDisketteName, sDiskettePath, true)) {
if (drive.disk) {
if (sDiskettePath) {
this.addDiskHistory(sDisketteName, sDiskettePath, drive.disk);
@ -1103,7 +1110,7 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
}
/*
* TODO: If loadDiskette() returned true, then this can happen immediately. Otherwise, loadDiskette()
* TODO: If loadDrive() returned true, then this can happen immediately. Otherwise, loadDrive()
* will have merely "queued up" the load request and drive.disk won't be ready yet, so figure out how/when
* we can properly restore drive.sector in that case.
*/
@ -1288,7 +1295,7 @@ FDC.prototype.autoMount = function(fRemount)
*/
var iDrive = sDrive.charCodeAt(0) - 0x41;
if (iDrive >= 0 && iDrive < this.aDrives.length) {
if (!this.loadDiskette(iDrive, sDisketteName, sDiskettePath, true) && fRemount) {
if (!this.loadDrive(iDrive, sDisketteName, sDiskettePath, true) && fRemount) {
this.setReady(false);
}
continue;
@ -1342,16 +1349,16 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
if (DEBUG) this.println("loading disk " + sDiskettePath + "...");
while (this.loadDiskette(iDrive, sDisketteName, sDiskettePath, false, file) < 0) {
while (this.loadDrive(iDrive, sDisketteName, sDiskettePath, false, file) < 0) {
if (!window.confirm("Click OK to reload the original disk.\n(WARNING: All disk changes will be discarded)")) {
return;
}
/*
* So here's the story: loadDiskette() returned true, which it does ONLY if the specified disk is already
* So here's the story: loadDrive() returned true, which it does ONLY if the specified disk is already
* mounted, AND the user clicked OK to reload the original disk image. So we must toss any history we have
* for the disk, unload it, and then loop back around to loadDiskette().
* for the disk, unload it, and then loop back around to loadDrive().
*
* loadDiskette() should NEVER return true the second time, since no disk is loaded. In other words,
* loadDrive() should NEVER return true the second time, since no disk is loaded. In other words,
* this isn't really a loop so much as a one-time retry operation.
*/
this.removeDiskHistory(sDisketteName, sDiskettePath);
@ -1363,24 +1370,24 @@ FDC.prototype.loadSelectedDrive = function(sDisketteName, sDiskettePath, file)
};
/**
* mountDiskette(iDrive, sDisketteName, sDiskettePath)
* mountDrive(iDrive, sDisketteName, sDiskettePath)
*
* @this {FDC}
* @param {number} iDrive
* @param {string} sDisketteName
* @param {string} sDiskettePath
*/
FDC.prototype.mountDiskette = function(iDrive, sDisketteName, sDiskettePath)
FDC.prototype.mountDrive = function(iDrive, sDisketteName, sDiskettePath)
{
var drive = this.aDrives[iDrive];
this.unloadDrive(iDrive, true, true);
drive.fLocal = true;
var disk = new Disk(this, drive, DiskAPI.MODE.PRELOAD);
this.doneLoadDiskette(drive, disk, sDisketteName, sDiskettePath, true);
this.doneLoadDrive(drive, disk, sDisketteName, sDiskettePath, true);
};
/**
* loadDiskette(iDrive, sDisketteName, sDiskettePath, fAutoMount, file)
* loadDrive(iDrive, sDisketteName, sDiskettePath, fAutoMount, file)
*
* NOTE: If sDiskettePath is already loaded in the drive, nothing needs to be done.
*
@ -1392,7 +1399,7 @@ FDC.prototype.mountDiskette = function(iDrive, sDisketteName, sDiskettePath)
* @param {File} [file] is set if there's an associated File object
* @return {number} 1 if diskette loaded, 0 if queued up (or busy), -1 if already loaded
*/
FDC.prototype.loadDiskette = function(iDrive, sDisketteName, sDiskettePath, fAutoMount, file)
FDC.prototype.loadDrive = function(iDrive, sDisketteName, sDiskettePath, fAutoMount, file)
{
var drive = this.aDrives[iDrive];
if (sDiskettePath) {
@ -1419,7 +1426,7 @@ FDC.prototype.loadDiskette = function(iDrive, sDisketteName, sDiskettePath, fAut
}
drive.fLocal = !!file;
var disk = new Disk(this, drive, DiskAPI.MODE.PRELOAD);
if (!disk.load(sDisketteName, sDiskettePath, file, this.doneLoadDiskette)) {
if (!disk.load(sDisketteName, sDiskettePath, file, this.doneLoadDrive)) {
return 0;
}
return 1;
@ -1429,7 +1436,7 @@ FDC.prototype.loadDiskette = function(iDrive, sDisketteName, sDiskettePath, fAut
};
/**
* doneLoadDiskette(drive, disk, sDisketteName, sDiskettePath, fAutoMount)
* doneLoadDrive(drive, disk, sDisketteName, sDiskettePath, fAutoMount)
*
* @this {FDC}
* @param {Object} drive
@ -1438,7 +1445,7 @@ FDC.prototype.loadDiskette = function(iDrive, sDisketteName, sDiskettePath, fAut
* @param {string} sDiskettePath
* @param {boolean} [fAutoMount]
*/
FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDisketteName, sDiskettePath, fAutoMount)
FDC.prototype.doneLoadDrive = function onFDCLoadNotify(drive, disk, sDisketteName, sDiskettePath, fAutoMount)
{
var aDiskInfo;
@ -1481,7 +1488,7 @@ FDC.prototype.doneLoadDiskette = function onFDCLoadNotify(drive, disk, sDiskette
this.addDiskHistory(sDisketteName, sDiskettePath, disk);
/*
* For a local disk (ie, one loaded via mountDiskette()), the disk.restore() performed by addDiskHistory()
* For a local disk (ie, one loaded via mountDrive()), the disk.restore() performed by addDiskHistory()
* may have altered the disk geometry, so refresh the disk info.
*/
aDiskInfo = disk.info();

View file

@ -80,43 +80,23 @@ function BusPDP11(parmsBus, cpu, dbg)
* It is managed by setIOPageRange(). reset() establishes the default (16).
*/
this.nIOPageRange = 0; // zero means no IOPAGE access (yet)
this.prevIOPageBlocks = []; // this saves any memory blocks we had to replace with IOPAGE blocks
this.realIOPageBlocks = null; // this saves the memory blocks allocated for IOPAGE, so we can reuse them
this.aIOPrevBlocks = []; // this saves any previous blocks we had to replace with IOPAGE blocks
this.aIOPageBlocks = null; // this saves the memory blocks allocated for IOPAGE, so we can reuse them
/*
* Compute all BusPDP11 memory block parameters, based on the width of the bus. The entire
* address space is divided into blocks, using a block size that is (hopefully) appropriate to
* the bus width. The following table summarizes our (original) simplistic calculations:
* Compute all BusPDP11 memory block parameters now, based on the width of the bus.
*
* Bus Width Block Shift Block Size
* --------- ----------- ----------
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
* 18 bits (256Kb address space): 11 2Kb (128 maximum blocks)
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
* 22 bits (4Mb address space): 13 8Kb (512 maximum blocks)
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
* Note that all PCjs machines divide their address space into blocks, using a block size appropriate for
* the machine's bus width. This allows us to efficiently allocate the entire address space, by reusing blocks
* as appropriate, and to define to different address behaviors on a block-granular level.
*
* The coarser block granularities (ie, 16Kb and 32Kb) may cause problems for certain RAM and/or ROM
* allocations that are contiguous but are allocated out of order, or that have different controller
* requirements. Your choices, for the moment, are either to ensure the allocations are performed in
* order, or to choose smaller nBlockShift values (at the expense of a generating a larger block array).
*/
this.addrTotal = Math.pow(2, this.nBusWidth);
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
/*
* WARNING: Instead of dynamically calculating nBlockShift based on nBusWidth, as described above, we
* now force nBlockSize to IOPAGE_LENGTH, because that's what our IOController functions currently assume.
*
* this.nBlockShift = (this.nBusWidth >> 1) + 2;
* if (this.nBlockShift < 10) this.nBlockShift = 10;
* if (this.nBlockShift > 15) this.nBlockShift = 15;
* this.nBlockSize = 1 << this.nBlockShift;
* For PDPjs machines, the ideal block size is 8Kb (IOPAGE_LENGTH), the size of the IOPAGE on all PDP-11 machines;
* as a result, our IOController functions assume that all incoming offsets are within a single 8Kb block.
*/
this.addrTotal = 1 << this.nBusWidth;
this.nBusMask = (this.addrTotal - 1);
this.nBlockSize = BusPDP11.IOPAGE_LENGTH;
this.nBlockShift = Math.log2(this.nBlockSize); // ES6 ALERT (alternatively: Math.log(this.nBlockSize) / Math.LN2)
this.nBlockLen = this.nBlockSize >> 2;
this.nBlockLimit = this.nBlockSize - 1;
this.nBlockTotal = (this.addrTotal / this.nBlockSize) | 0;
@ -137,14 +117,16 @@ function BusPDP11(parmsBus, cpu, dbg)
* Memory access handlers must service the entire block; see the setAccess() function in the Memory
* component for details.
*
* Finally, for debugging purposes, if an I/O address has a symbolic name, it will be saved here:
* Finally, for debugging purposes, if an I/O address has a symbolic name and message category,
* they will be saved here:
*
* [4]: symbolic name of I/O address
* [5]: message category
*
* UPDATE: The Debugger wants to piggy-back on these arrays to indicate addresses for which it wants
* notification. In those cases, the following additional element will be set:
*
* [5]: true to break on I/O, false to ignore I/O
* [6]: true to break on I/O, false to ignore I/O
*
* The false case is important if fIOBreakAll is set, because it allows the Debugger to selectively
* ignore specific addresses.
@ -152,6 +134,8 @@ function BusPDP11(parmsBus, cpu, dbg)
this.aIOHandlers = [];
this.fIOBreakAll = false;
this.nDisableFaults = 0;
this.fFault = false;
this.cbRAM = 0;
/*
* Array of RESET notification handlers registered by Device components.
@ -202,14 +186,16 @@ BusPDP11.IOHANDLER = {
WRITE_BYTE: 1,
READ_WORD: 2,
WRITE_WORD: 3,
NAME: 4
NAME: 4,
MSG_CATEGORY: 5,
DBG_BREAK: 6
};
/*
* These are our custom IOController functions for all IOPAGE accesses. They look up the IOPAGE
* offset in the aIOHandlers table, and if an entry exists, they use the appropriate IOHANDLER indexes
* (above) to locate the registered read/write handlers. If no handler is found, then unknownAccess()
* is called, triggering a trap -- unless traps are disabled because direct access was requested
* (above) to locate the registered read/write handlers. If no handler is found, then fault() will
* be called, triggering a trap -- unless traps are disabled because direct access was requested
* (eg, by the Debugger).
*
* Handlers receive the original IOPAGE address that was used, although in most cases, it's ignored,
@ -233,7 +219,7 @@ BusPDP11.IOHANDLER = {
* Unlike regular Memory blocks, IOPAGE accesses permit word accesses on ODD addresses; that works
* just fine by registering WORD handlers for the appropriate ODD addresses. For BYTE accesses, it
* depends. For CPU register addresses, addIOHandlers() installs special byte handlers that perform
* either a simple word read or write. Other addresses must be handled on case-by-case basis.
* either a simple word read or write. Other addresses must be handled on a case-by-case basis.
*
* TODO: Another small potential improvement would be for addIOHandlers() to install fallbacks for ALL
* missing handlers, in both the ODD and EVEN cases, so there's never a need to check each function index
@ -257,33 +243,48 @@ BusPDP11.IOController = {
var b = -1;
var bus = this.controller;
var afn = bus.aIOHandlers[off];
/*
* Since addr is primarily used to advise an I/O handler of the target IOPAGE address, and since we don't want
* our handlers to worry about the current IOPAGE location, we truncate addr to 16 bits (the IOPAGE's lowest location).
*/
var addrMasked = addr & 0xffff;
if (afn) {
if (afn[BusPDP11.IOHANDLER.READ_BYTE]) {
b = afn[BusPDP11.IOHANDLER.READ_BYTE](addr);
b = afn[BusPDP11.IOHANDLER.READ_BYTE](addrMasked);
} else if (afn[BusPDP11.IOHANDLER.READ_WORD]) {
if (!(addr & 0x1)) {
b = afn[BusPDP11.IOHANDLER.READ_WORD](addr) & 0xff;
if (!(addrMasked & 0x1)) {
b = afn[BusPDP11.IOHANDLER.READ_WORD](addrMasked) & 0xff;
} else {
b = afn[BusPDP11.IOHANDLER.READ_WORD](addr & ~0x1) >> 8;
b = afn[BusPDP11.IOHANDLER.READ_WORD](addrMasked & ~0x1) >> 8;
}
}
} else if (addr & 0x1) {
} else if (addrMasked & 0x1) {
afn = bus.aIOHandlers[off & ~0x1];
if (afn) {
if (afn[BusPDP11.IOHANDLER.READ_WORD]) {
b = afn[BusPDP11.IOHANDLER.READ_WORD](addr & ~0x1) >> 8;
b = afn[BusPDP11.IOHANDLER.READ_WORD](addrMasked & ~0x1) >> 8;
} else if (afn[BusPDP11.IOHANDLER.READ_BYTE]) {
/*
* WARNING: This is an unusual fall-back, because we're trying to read an ODD byte
* access using a BYTE handler registered for EVEN bytes. But if that's all we've got,
* then presumably the handler is prepared for it (certainly, readROMByte() is).
*/
b = afn[BusPDP11.IOHANDLER.READ_BYTE](addrMasked)
}
}
}
if (b >= 0) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true, true);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true, !bus.nDisableFaults);
}
return b;
}
b = bus.unknownAccess(addr, true);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted read access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b), true, true);
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.READ_BYTE);
b = 0xff;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted read access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b), true, !bus.nDisableFaults);
}
return b;
},
@ -302,12 +303,19 @@ BusPDP11.IOController = {
var fWrite = false;
var bus = this.controller;
var afn = bus.aIOHandlers[off];
/*
* Since addr is primarily used to advise an I/O handler of the target IOPAGE address, and since we don't want
* our handlers to worry about the current IOPAGE location, we truncate addr to 16 bits (the IOPAGE's lowest location).
*/
var addrMasked = addr & 0xffff;
if (afn) {
/*
* If a writeByte() handler exists, call it; we're done.
*/
if (afn[BusPDP11.IOHANDLER.WRITE_BYTE]) {
afn[BusPDP11.IOHANDLER.WRITE_BYTE](b, addr);
afn[BusPDP11.IOHANDLER.WRITE_BYTE](b, addrMasked);
fWrite = true;
}
/*
@ -320,15 +328,15 @@ BusPDP11.IOController = {
*/
else if (afn[BusPDP11.IOHANDLER.WRITE_WORD]) {
w = afn[BusPDP11.IOHANDLER.READ_WORD]? afn[BusPDP11.IOHANDLER.READ_WORD](0) : 0;
if (!(addr & 0x1)) {
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & ~0xff) | b, addr);
if (!(addrMasked & 0x1)) {
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & ~0xff) | b, addrMasked);
fWrite = true;
} else {
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & 0xff) | (b << 8), addr & ~0x1);
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & 0xff) | (b << 8), addrMasked & ~0x1);
fWrite = true;
}
}
} else if (addr & 0x1) {
} else if (addrMasked & 0x1) {
/*
* If no handler existed, and this address was odd, then perhaps a handler exists for the even address;
* if so, call the readWord() handler first to get the original data, then call writeWord() with the new
@ -341,22 +349,29 @@ BusPDP11.IOController = {
afn = bus.aIOHandlers[off & ~0x1];
if (afn) {
if (afn[BusPDP11.IOHANDLER.WRITE_WORD]) {
addr &= ~0x1;
addrMasked &= ~0x1;
w = afn[BusPDP11.IOHANDLER.READ_WORD]? afn[BusPDP11.IOHANDLER.READ_WORD](0) : 0;
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & 0xff) | (b << 8), addr);
afn[BusPDP11.IOHANDLER.WRITE_WORD]((w & 0xff) | (b << 8), addrMasked);
fWrite = true;
} else if (afn[BusPDP11.IOHANDLER.WRITE_BYTE]) {
/*
* WARNING: This is an unusual fall-back, because we're trying to write an ODD byte
* access using a BYTE handler registered for EVEN bytes. But if that's all we've got,
* then presumably the handler is prepared for it (certainly, writeROMByte() is).
*/
afn[BusPDP11.IOHANDLER.WRITE_BYTE](b, addrMasked);
}
}
}
if (fWrite) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true, true);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true, !bus.nDisableFaults);
}
return;
}
bus.unknownAccess(addr, true, b);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted write access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b), true, true);
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.WRITE_BYTE);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted write access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b), true, !bus.nDisableFaults);
}
},
@ -373,22 +388,30 @@ BusPDP11.IOController = {
var w = -1;
var bus = this.controller;
var afn = bus.aIOHandlers[off];
/*
* Since addr is primarily used to advise an I/O handler of the target IOPAGE address, and since we don't want
* our handlers to worry about the current IOPAGE location, we truncate addr to 16 bits (the IOPAGE's lowest location).
*/
var addrMasked = addr & 0xffff;
if (afn) {
if (afn[BusPDP11.IOHANDLER.READ_WORD]) {
w = afn[BusPDP11.IOHANDLER.READ_WORD](addr);
w = afn[BusPDP11.IOHANDLER.READ_WORD](addrMasked);
} else if (afn[BusPDP11.IOHANDLER.READ_BYTE]) {
w = afn[BusPDP11.IOHANDLER.READ_BYTE](addr) | (afn[BusPDP11.IOHANDLER.READ_BYTE](addr + 1) << 8);
w = afn[BusPDP11.IOHANDLER.READ_BYTE](addrMasked) | (afn[BusPDP11.IOHANDLER.READ_BYTE](addrMasked + 1) << 8);
}
}
if (w >= 0) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true, true);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true, !bus.nDisableFaults);
}
return w;
}
w = bus.unknownAccess(addr, false);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted read access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w), true, true);
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.READ_WORD);
w = 0xffff;
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted read access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w), true, !bus.nDisableFaults);
}
return w;
},
@ -406,25 +429,32 @@ BusPDP11.IOController = {
var fWrite = false;
var bus = this.controller;
var afn = bus.aIOHandlers[off];
/*
* Since addr is primarily used to advise an I/O handler of the target IOPAGE address, and since we don't want
* our handlers to worry about the current IOPAGE location, we truncate addr to 16 bits (the IOPAGE's lowest location).
*/
var addrMasked = addr & 0xffff;
if (afn) {
if (afn[BusPDP11.IOHANDLER.WRITE_WORD]) {
afn[BusPDP11.IOHANDLER.WRITE_WORD](w, addr);
afn[BusPDP11.IOHANDLER.WRITE_WORD](w, addrMasked);
fWrite = true;
} else if (afn[BusPDP11.IOHANDLER.WRITE_BYTE]) {
afn[BusPDP11.IOHANDLER.WRITE_BYTE](w & 0xff, addr);
afn[BusPDP11.IOHANDLER.WRITE_BYTE](w >> 8, addr + 1);
afn[BusPDP11.IOHANDLER.WRITE_BYTE](w & 0xff, addrMasked);
afn[BusPDP11.IOHANDLER.WRITE_BYTE](w >> 8, addrMasked + 1);
fWrite = true;
}
}
if (fWrite) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true, true);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage(afn[BusPDP11.IOHANDLER.NAME] + ".writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true, !bus.nDisableFaults);
}
return;
}
bus.unknownAccess(addr, false, w);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
this.dbg.printMessage("warning: unconverted write access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w), true, true);
bus.fault(addr, PDP11.CPUERR.TIMEOUT, PDP11.ACCESS.WRITE_WORD);
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(afn[BusPDP11.IOHANDLER.MSG_CATEGORY])) {
this.dbg.printMessage("warning: unconverted write access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w), true, !bus.nDisableFaults);
}
}
};
@ -465,18 +495,20 @@ BusPDP11.prototype.setIOPageRange = function(nRange)
var addr;
if (this.nIOPageRange) {
addr = (1 << this.nIOPageRange) - BusPDP11.IOPAGE_LENGTH;
this.setMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH, this.prevIOPageBlocks);
this.setMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH, this.aIOPrevBlocks);
this.nIOPageRange = 0;
}
if (nRange) {
this.nIOPageRange = nRange;
addr = (1 << nRange) - BusPDP11.IOPAGE_LENGTH;
this.prevIOPageBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
if (this.realIOPageBlocks) {
this.setMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH, this.realIOPageBlocks);
addr = (1 << nRange);
this.nBusMask = (addr - 1);
addr -= BusPDP11.IOPAGE_LENGTH;
this.aIOPrevBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
if (this.aIOPageBlocks) {
this.setMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH, this.aIOPageBlocks);
} else {
this.addMemory(addr, BusPDP11.IOPAGE_LENGTH, MemoryPDP11.TYPE.CONTROLLER, this);
this.realIOPageBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
this.aIOPageBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
}
}
}
@ -494,7 +526,7 @@ BusPDP11.prototype.setIOPageRange = function(nRange)
BusPDP11.prototype.getControllerBuffer = function(addr)
{
/*
* No buffer is required for the IOPAGE; all accesses go to registered I/O handlers or to unknownAccess().
* No buffer is required for the IOPAGE; all accesses go to registered I/O handlers or to fault().
*/
return [null, 0];
};
@ -538,32 +570,6 @@ BusPDP11.prototype.reset = function()
this.setIOPageRange(16);
};
/**
* unknownAccess(addr, fByte, data)
*
* This is our default I/O handler, called when there's an IOPAGE access without a corresponding entry in aIOHandlers.
*
* @this {BusPDP11}
* @param {number} addr (ie, an IOPAGE address)
* @param {boolean} [fByte] (true if byte access, otherwise word)
* @param {number} [data] (undefined if read, otherwise write)
* @return {number}
*/
BusPDP11.prototype.unknownAccess = function(addr, fByte, data)
{
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.WARN)) {
/*
* TODO: For 22-bit machines, let's display addr as a 3-byte value (for a total of 9 octal digits)
*/
this.dbg.printMessage("warning: unknown I/O access (" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(data, fByte?1:2) + ")", true, true);
this.dbg.stopInstruction();
}
if (!this.nDisableFaults) {
this.cpu.trap(PDP11.TRAP.BUS_ERROR, addr);
}
return 0;
};
/**
* powerUp(data, fRepower)
*
@ -670,7 +676,10 @@ BusPDP11.prototype.addMemory = function(addr, size, type, controller)
}
if (sizeLeft <= 0) {
this.status(str.toDec(size / 1024) + "Kb " + MemoryPDP11.TYPE_NAMES[type] + " at " + str.toOct(addr));
if (type == MemoryPDP11.TYPE.RAM) {
this.cbRAM += size;
}
this.status((size >> 10) + "Kb " + MemoryPDP11.TYPE_NAMES[type] + " at " + str.toOct(addr));
return true;
}
@ -701,18 +710,26 @@ BusPDP11.prototype.cleanMemory = function(addr, size)
};
/**
* zeroMemory(addr, size)
* zeroMemory(addr, size, pattern)
*
* @this {BusPDP11}
* @param {number} addr
* @param {number} size
* @param {number} [pattern]
*/
BusPDP11.prototype.zeroMemory = function(addr, size)
BusPDP11.prototype.zeroMemory = function(addr, size, pattern)
{
var off = addr & this.nBlockLimit;
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
this.aMemBlocks[iBlock].zero(off, size);
var block = this.aMemBlocks[iBlock];
if (block.controller) {
if (this.aIOPageBlocks && this.aIOPageBlocks.length == this.aIOPrevBlocks.length) {
var i = this.aIOPageBlocks.indexOf(block);
if (i >= 0) block = this.aIOPrevBlocks[i];
}
}
if (block) block.zero(off, size, pattern);
size -= this.nBlockSize;
iBlock++;
off = 0;
@ -911,13 +928,21 @@ BusPDP11.prototype.setMemoryBlocks = function(addr, size, aBlocks, type)
*/
BusPDP11.prototype.getByte = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
return this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
};
/**
* getByteDirect(addr)
*
* This is useful for the Debugger and other components that want to bypass getByte() breakpoint detection.
* This is useful for the Debugger and other components that want to access physical memory without side-effects.
*
* @this {BusPDP11}
* @param {number} addr is a physical address
@ -925,6 +950,15 @@ BusPDP11.prototype.getByte = function(addr)
*/
BusPDP11.prototype.getByteDirect = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
this.fFault = false;
this.nDisableFaults++;
var b = this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].readByteDirect(addr & this.nBlockLimit, addr);
this.nDisableFaults--;
@ -940,6 +974,14 @@ BusPDP11.prototype.getByteDirect = function(addr)
*/
BusPDP11.prototype.getWord = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
@ -959,9 +1001,18 @@ BusPDP11.prototype.getWord = function(addr)
*/
BusPDP11.prototype.getWordDirect = function(addr)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
var w;
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
this.fFault = false;
this.nDisableFaults++;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
w = this.aMemBlocks[iBlock++].readByteDirect(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByteDirect(0, addr + 1) << 8);
@ -981,6 +1032,14 @@ BusPDP11.prototype.getWordDirect = function(addr)
*/
BusPDP11.prototype.setByte = function(addr, b)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
};
@ -996,6 +1055,15 @@ BusPDP11.prototype.setByte = function(addr, b)
*/
BusPDP11.prototype.setByteDirect = function(addr, b)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
this.fFault = false;
this.nDisableFaults++;
this.aMemBlocks[(addr & this.nBusMask) >>> this.nBlockShift].writeByteDirect(addr & this.nBlockLimit, b & 0xff, addr);
this.nDisableFaults--;
@ -1010,6 +1078,14 @@ BusPDP11.prototype.setByteDirect = function(addr, b)
*/
BusPDP11.prototype.setWord = function(addr, w)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
@ -1032,8 +1108,17 @@ BusPDP11.prototype.setWord = function(addr, w)
*/
BusPDP11.prototype.setWordDirect = function(addr, w)
{
/*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so we must pass the address through the
* UNIBUS relocation map.
*/
if (addr >= BusPDP11.IOPAGE_UNIBUS) {
addr = this.cpu.mapUnibus(addr);
}
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nBusMask) >>> this.nBlockShift;
this.fFault = false;
this.nDisableFaults++;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByteDirect(off, w & 0xff, addr);
@ -1164,6 +1249,27 @@ BusPDP11.prototype.restoreMemory = function(a)
return true;
};
/**
* getMemorySize(type)
*
* NOTE: The original pdp11.js defined MAX_MEMORY as IOBASE_UNIBUS - 16384, where IOBASE_UNIBUS
* is 4Mb less 256Kb, and then it subtracted another 16Kb so that BSD 2.9 could boot.
*
* @this {BusPDP11}
* @param {number} type is one of the MemoryPDP11.TYPE constants (only RAM is currently supported)
* @return {number} (size of initial allocation, in bytes)
*/
BusPDP11.prototype.getMemorySize = function(type)
{
var cb = 0;
switch(type) {
case MemoryPDP11.TYPE.RAM:
cb = this.cbRAM;
break;
}
return cb;
};
/**
* addIOHandlers(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName)
*
@ -1171,6 +1277,11 @@ BusPDP11.prototype.restoreMemory = function(a)
* relative to the starting IOPAGE address, but they can also be absolute; we simply mask all addresses with
* IOPAGE_MASK.
*
* CAVEATS: If a conflict is reported, a partial set of handlers may still have been added. There is no mechanism
* for removing handlers, since this is considered an initialization function. And finally, when a range of addresses
* is used, each successive address is advanced by 2, so if you really want to add a handler for a "+1" (usually odd)
* address, then you must add it individually.
*
* @this {BusPDP11}
* @param {number} start address
* @param {number} end address
@ -1178,31 +1289,37 @@ BusPDP11.prototype.restoreMemory = function(a)
* @param {function(number,number)|null|undefined} fnWriteByte
* @param {function(number)|null|undefined} fnReadWord
* @param {function(number,number)|null|undefined} fnWriteWord
* @param {number} [msgCategory]
* @param {string} [sName]
* @return {boolean} (true if entire range successfully registered, false if any conflicts)
*/
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName)
BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, msgCategory, sName)
{
for (var addr = start; addr <= end; addr += 2) {
var off = addr & BusPDP11.IOPAGE_MASK;
if (this.aIOHandlers[off] !== undefined) {
Component.warning("I/O address already registered: " + str.toHexLong(addr));
continue;
return false;
}
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName || "unknown", false];
this.aIOHandlers[off] = [fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, sName || "unknown", msgCategory, false];
if (MAXDEBUG) this.log("addIOHandlers(" + str.toHexLong(addr) + ")");
}
return true;
};
/**
* addIOTable(component, table)
* addIOTable(component, table, msgCategory, sName)
*
* Add I/O notification handlers from the specified table (a batch version of addIOHandlers).
*
* @this {BusPDP11}
* @param {Component} component
* @param {Object} table
* @param {number} [msgCategory] (default is BUS)
* @param {string} [sName]
* @return {boolean} (true if entire range successfully registered, false if any conflicts)
*/
BusPDP11.prototype.addIOTable = function(component, table)
BusPDP11.prototype.addIOTable = function(component, table, msgCategory, sName)
{
for (var port in table) {
var addr = +port;
@ -1212,16 +1329,17 @@ BusPDP11.prototype.addIOTable = function(component, table)
* Don't install (ie, ignore) handlers for I/O addresses that are defined with a model number
* that is "greater than" than the current model.
*/
if (afn[5] && afn[5] > this.cpu.model) continue;
if (afn[6] && afn[6] > this.cpu.model) continue;
var fnReadByte = afn[0]? afn[0].bind(component) : null;
var fnWriteByte = afn[1]? afn[1].bind(component) : null;
var fnReadWord = afn[2]? afn[2].bind(component) : null;
var fnWriteWord = afn[3]? afn[3].bind(component) : null;
var nRegs = afn[5] || 1;
/*
* As discussed in the IOController comments above, when handlers are being registered for the following
* addresses, we must install different fallback handlers for all BYTE accesses.
* As discussed in the IOController comments above, when handlers are being registered for these
* BYTE-granular UNIBUS addresses, we must install custom fallback handlers for all BYTE accesses.
*/
if (addr >= PDP11.UNIBUS.R0SET0 && addr <= PDP11.UNIBUS.R6USER) {
if (!fnReadByte && fnReadWord) {
@ -1239,8 +1357,16 @@ BusPDP11.prototype.addIOTable = function(component, table)
}(fnWriteWord);
}
}
this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, afn[4]);
var sReg = afn[4];
for (var iReg = 0; iReg < nRegs; iReg++, addr += 2) {
if (sReg && nRegs > 1) sReg = afn[4] + iReg;
if (!this.addIOHandlers(addr, addr, fnReadByte, fnWriteByte, fnReadWord, fnWriteWord, msgCategory || MessagesPDP11.BUS, sReg || sName)) {
return false;
}
}
}
return true;
};
/**
@ -1255,25 +1381,42 @@ BusPDP11.prototype.addResetHandler = function(fnReset)
};
/**
* fault(addr, access)
* fault(addr, err, access)
*
* Memory interface for signaling alignment errors, invalid memory
* Bus interface for signaling alignment errors, invalid memory, etc.
*
* @this {BusPDP11}
* @param {number} addr
* @param {number} [err]
* @param {number} [access] (for diagnostic purposes only)
*/
BusPDP11.prototype.fault = function(addr, access)
BusPDP11.prototype.fault = function(addr, err, access)
{
this.fFault = true;
if (!this.nDisableFaults) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.FAULT)) {
this.dbg.printMessage("memory fault (" + access + ") on address " + this.dbg.toStrBase(addr), true, true);
this.dbg.stopInstruction();
}
if (err) this.cpu.regErr |= err;
this.cpu.trap(PDP11.TRAP.BUS_ERROR, addr);
}
};
/**
* checkFault()
*
* This also serves as a clearFault() function.
*
* @this {BusPDP11}
* @return {boolean}
*/
BusPDP11.prototype.checkFault = function()
{
var f = this.fFault;
this.fFault = false;
return f;
};
/**
* reportError(errNum, addr, size, fQuiet)
*

View file

@ -744,6 +744,7 @@ ComputerPDP11.prototype.donePowerOn = function(aParms)
* TODO: Do we not care about the return value here? (ie, is checking fRestoreError sufficient)?
*/
this.powerRestore(this.cpu, stateComputer, fRepower, fRestore);
this.updateDisplays();
this.cpu.autoStart();
}
@ -955,26 +956,31 @@ ComputerPDP11.prototype.powerOff = function(fSave, fShutdown)
* allocated the Bus object ourselves, after all the other components were allocated, it ends
* up near the end of Component's list of components. Hence the special case for this.bus below.
*
* Ditto for the CPU, in part because if the Front Panel resets before the CPU, it will end up
* snapping/displaying the PC as of the last instruction executed, before the CPU resets the PC,
* causing the Front Panel to display a stale address when we call updateDisplays() at the end.
*
* @this {ComputerPDP11}
*/
ComputerPDP11.prototype.reset = function()
{
if (this.bus && this.bus.reset) {
/*
* TODO: Why does WebStorm think that this.bus.type is undefined? The base class (Component)
* constructor defines it.
*/
this.printMessage("Resetting " + this.bus.type);
this.bus.reset();
}
if (this.cpu && this.cpu.reset) {
this.printMessage("Resetting " + this.cpu.type);
this.cpu.reset();
}
var aComponents = Component.getComponents(this.id);
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
var component = aComponents[iComponent];
if (component !== this && component !== this.bus && component.reset) {
if (component !== this && component !== this.bus && component !== this.cpu && component.reset) {
this.printMessage("Resetting " + component.type);
component.reset();
}
}
this.updateDisplays(-1);
};
/**
@ -999,7 +1005,7 @@ ComputerPDP11.prototype.start = function(ms, nCycles)
component.start(ms, nCycles);
}
}
this.updateStatus(true);
this.updateDisplays(-1);
};
/**
@ -1024,7 +1030,45 @@ ComputerPDP11.prototype.stop = function(ms, nCycles)
component.stop(ms, nCycles);
}
}
this.updateStatus(true);
this.updateDisplays(-1);
};
/**
* updateDisplays(nUpdate)
*
* TODO: Notify all components with an updateDisplay() method that the computer's state has changed (not
* just the hard-coded ones below).
*
* If any DOM controls were bound to the CPU, then we need to call its updateDisplay() handler; if there are no
* such bindings, then cpu.updateDisplay() does nothing.
*
* Similarly, if there's a Panel, then we need to call its updateDisplay() handler, in case it created its own canvas
* and implemented its own register display (eg, dumpRegisters()); if not, then panel.updateDisplay() also does nothing.
*
* In practice, there will *either* be a Panel with a custom canvas *or* a set of DOM controls bound to the CPU *or*
* neither. In theory, there could be BOTH, but that would be unusual.
*
* TODO: Consider alternate approaches to these largely register-oriented display updates. Ordinarily, we like to
* separate logic from presentation, and currently the CPUState contains both, since it's the component that intimately
* knows the names, number, sizes, etc, of all the active registers. The Panel component is the logical candidate,
* but Panel is an optional component; it's often the case that only machines that include the Debugger also include
* Panel.
*
* @this {ComputerPDP11}
* @param {number} [nUpdate] (1 for periodic, -1 for forced, 0 or undefined otherwise)
*/
ComputerPDP11.prototype.updateDisplays = function(nUpdate)
{
/*
* nUpdate is generally set to -1 whenever the CPU is transitioning to/from a running state, in which case
* cpu.updateDisplay() will definitely want to hide/show register contents; however, at other times, when the
* CPU is running, constantly updating the DOM controls too frequently can adversely impact overall performance.
*
* nUpdate will also be -1 whenever the Debugger has modified the state of the machine, implying that we're
* not sure what, if anything, actually changed.
*/
if (this.cpu) this.cpu.updateDisplay(nUpdate);
if (this.panel) this.panel.updateDisplay(nUpdate);
};
/**
@ -1429,41 +1473,6 @@ ComputerPDP11.prototype.setFocus = function(fScroll)
}
};
/**
* updateStatus(fForce)
*
* If any DOM controls were bound to the CPU, then we need to call its updateStatus() handler; if there are no
* such bindings, then cpu.updateStatus() does nothing.
*
* Similarly, if there's a Panel, then we need to call its updateStatus() handler, in case it created its own canvas
* and implemented its own register display (eg, dumpRegisters()); if not, then panel.updateStatus() also does nothing.
*
* In practice, there will *either* be a Panel with a custom canvas *or* a set of DOM controls bound to the CPU *or*
* neither. In theory, there could be BOTH, but that would be unusual.
*
* TODO: Consider alternate approaches to these largely register-oriented display updates. Ordinarily, we like to
* separate logic from presentation, and currently the CPUState contains both, since it's the component that intimately
* knows the names, number, sizes, etc, of all the active registers. The Panel component is the logical candidate,
* but Panel is an optional component; generally, only machines that include Debugger also include Panel.
*
* @this {ComputerPDP11}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
*/
ComputerPDP11.prototype.updateStatus = function(fForce)
{
/*
* fForce is generally set to true whenever the CPU is transitioning to/from a running state, in which case
* cpu.updateStatus() will definitely want to hide/show register contents; however, at other times, when the
* CPU is running, constantly updating the DOM controls too frequently can adversely impact overall performance.
*
* So fForce serves as a hint to help cpu.updateStatus() make a more informed decision. panel.updateStatus()
* currently doesn't care, on the theory that canvas updates should be significantly faster than DOM updates,
* but we still pass fForce on.
*/
if (this.cpu) this.cpu.updateStatus(fForce);
if (this.panel) this.panel.updateStatus(fForce);
};
/**
* ComputerPDP11.init()
*

View file

@ -283,7 +283,6 @@ CPUPDP11.prototype.powerUp = function(data, fRepower)
*
* this.flags.powered = true;
*/
this.cmp.updateStatus();
return true;
};
@ -506,15 +505,28 @@ CPUPDP11.prototype.setBinding = function(sType, sBinding, control, sValue)
};
/**
* updateStatus(fForce)
* updateDisplays(nUpdate)
*
* Some of the CPU bindings provide feedback and therefore need to be updated periodically. This is called
* via the Computer's updateStatus() handler several times per second; see YIELDS_PER_STATUS.
* Simpler wrapper around the Computer's updateDisplays() method.
*
* @this {CPUPDP11}
* @param {boolean} [fForce]
* @param {number} [nUpdate] (1 for periodic, -1 for forced, 0 or undefined otherwise)
*/
CPUPDP11.prototype.updateStatus = function(fForce)
CPUPDP11.prototype.updateDisplays = function(nUpdate)
{
if (this.cmp) this.cmp.updateDisplays(nUpdate);
};
/**
* updateDisplay(nUpdate)
*
* Some of the CPU bindings provide feedback and therefore need to be updated periodically.
* However, this should be called via the Computer's updateDisplays() interface, not directly.
*
* @this {CPUPDP11}
* @param {number} [nUpdate] (1 for periodic, -1 for forced, 0 or undefined otherwise)
*/
CPUPDP11.prototype.updateDisplay = function(nUpdate)
{
var controlSpeed = this.bindings["speed"];
if (controlSpeed) controlSpeed.textContent = this.getSpeedCurrent();
@ -1076,7 +1088,7 @@ CPUPDP11.prototype.runCPU = function()
nCycles = this.endBurst(true);
/*
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU first started).
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU started).
*/
this.nCyclesThisRun += nCycles;
this.nRunCycles += nCycles;
@ -1091,7 +1103,7 @@ CPUPDP11.prototype.runCPU = function()
if (this.nCyclesNextYield <= 0) {
this.nCyclesNextYield += this.nCyclesPerYield;
if (++this.nYieldsSinceStatusUpdate >= CPUPDP11.YIELDS_PER_STATUS) {
if (this.cmp) this.cmp.updateStatus();
this.updateDisplays();
this.nYieldsSinceStatusUpdate = 0;
}
break;
@ -1206,7 +1218,7 @@ CPUPDP11.prototype.yieldCPU = function()
* odd for those messages to show CPU state changes if the Control Panel, Video display, etc, does not,
* so I've added this call to try to keep things looking synchronized.
*/
this.cmp.updateStatus();
this.updateDisplays();
};
if (NODE) module.exports = CPUPDP11;

View file

@ -884,7 +884,7 @@ PDP11.opCLR = function(opCode)
*/
PDP11.opCLRB = function(opCode)
{
this.updateAllFlags(this.writeDstByte(opCode, 0));
this.updateAllFlags(this.writeDstByte(opCode, 0, PDP11.WRITE.BYTE));
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};
@ -1269,6 +1269,31 @@ PDP11.opMFPI = function(opCode)
this.nStepCycles -= (10 + 1);
};
/**
* opMFPT(opCode)
*
* 000007 MFPT - Move From Processor Type
*
* Loads R0 with a value indicating the processor type.
*
* R0 Hardware
* 1 PDP-11/44
* 3 PDP-11/24 (should be 2)
* 3 PDP-11/23
* 4 SBC-11/21
* 5 All J11 chips including 11/73, 11/83, 11/93
*
* @this {CPUStatePDP11}
* @param {number} opCode
*/
PDP11.opMFPT = function(opCode)
{
/*
* TODO: Review
*/
this.trap(PDP11.TRAP.RESERVED, PDP11.REASON.RESERVED);
};
PDP11.MOV_CYCLES = [
2 + 1, 8 + 1, 8 + 1, 11 + 2, 9 + 1, 12 + 2, 10 + 2, 13 + 3,
3 + 1, 8 + 1, 8 + 1, 11 + 2, 9 + 1, 12 + 2, 11 + 2, 14 + 3
@ -1301,7 +1326,7 @@ PDP11.opMOV = function(opCode)
PDP11.opMOVB = function(opCode)
{
var data = this.readSrcByte(opCode);
this.updateNZVFlags(this.writeDstByte(opCode, data, PDP11.WRITE.SIGNEXT) << 8);
this.updateNZVFlags(this.writeDstByte(opCode, data, PDP11.WRITE.SBYTE) << 8);
this.nStepCycles -= (this.dstMode? (8 + 1) + (this.srcReg && this.dstReg >= 6? 1 : 0) : (this.srcMode? (3 + 2) : (2 + 1)) + (this.dstReg == 7? 2 : 0));
};
@ -1411,11 +1436,10 @@ PDP11.opNOP = function(opCode)
PDP11.opRESET = function(opCode)
{
if (!(this.regPSW & PDP11.PSW.CMODE)) {
this.resetRegs();
this.bus.reset();
// display.data = this.regsGen[0]; // TODO: Review
this.resetRegs();
}
this.nStepCycles -= 667; // TODO: Review (but it's definitely a big number)
this.nStepCycles -= 667; // TODO: Review (but it's definitely a big number)
};
/**
@ -1755,13 +1779,13 @@ PDP11.opWAIT = function(opCode)
* NOTE: It's almost always a bad idea to add more checks to the inner stepCPU() loop, because every additional
* check can have a measurable (negative) impact on performance. Which is why it's important to use opFlags bits
* whenever possible, since we can test for multiple (up to 32) exceptional conditions with a single check.
*
* Finally, we used to update the machine's displays whenever transitioning to the WAIT state. However,
* it makes more sense to decouple display updates from specific instructions and rely on timers instead;
* the PDP-11 KW11 (60Hz Line Clock) timer is the perfect candidate. See device.js.
*
* if (!(this.opFlags & PDP11.OPFLAG.WAIT) && this.cmp) this.cmp.updateDisplays();
*/
if (!(this.opFlags & PDP11.OPFLAG.WAIT)) {
/*
* Since here we're actually transitioning to WAIT, let's update the Panel's LEDs (well, OK, among other things).
*/
this.cmp.updateStatus();
}
this.opFlags |= PDP11.OPFLAG.WAIT;
this.advancePC(-2);
this.nStepCycles -= 3;
@ -2271,7 +2295,7 @@ PDP11.aOp000X_1145 = [
PDP11.opIOT, // 0x0004 000004 11/20+ 9.3
PDP11.opRESET, // 0x0005 000005 11/20+ 20ms
PDP11.opRTT, // 0x0006 000006 11/45+
PDP11.opUndefined, // 0x0007
PDP11.opMFPT, // 0x0007 000007 TBD
PDP11.opUndefined, // 0x0008
PDP11.opUndefined, // 0x0009
PDP11.opUndefined, // 0x000A

View file

@ -200,10 +200,9 @@ CPUStatePDP11.prototype.initRegs = function()
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // user 3
];
this.unibusMap = [ // 32 unibus map registers
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
this.regsControl = [ // various control registers we don't really care about
this.regsControl = [ // various control registers (177740-177756) we don't really care about
0, 0, 0, 0, 0, 0, 0, 0
];
this.regMB = 0;
@ -242,10 +241,7 @@ CPUStatePDP11.prototype.resetRegs = function()
this.regMMR3 = 0; // 172516
this.mmuEnable = 0; // MMU enabled for PDP11.ACCESS.READ or PDP11.ACCESS.WRITE
this.mmuLastMode = 0;
this.mmuMask = 0x3ffff;
this.mmuMemorySize = BusPDP11.IOPAGE_18BIT;
this.resetTriggers();
if (this.bus) this.setMemoryAccess();
@ -282,6 +278,9 @@ CPUStatePDP11.prototype.setMemoryAccess = function()
/**
* getMMR0()
*
* 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 MMR0
* nonr leng read trap unus unus ena mnt cmp -mode- i/d --page-- enable
*
* @this {CPUStatePDP11}
* @return {number}
*/
@ -359,13 +358,7 @@ CPUStatePDP11.prototype.setMMR3 = function(newMMR3)
}
if (this.regMMR3 != newMMR3) {
this.regMMR3 = newMMR3;
if (newMMR3 & PDP11.MMR3.MMU_22BIT) {
this.mmuMask = 0x3fffff;
this.mmuMemorySize = BusPDP11.MAX_MEMORY;
} else {
this.mmuMask = 0x3ffff;
this.mmuMemorySize = BusPDP11.IOPAGE_18BIT;
}
this.mmuMask = (newMMR3 & PDP11.MMR3.MMU_22BIT)? 0x3fffff : 0x3ffff;
this.setMemoryAccess();
}
};
@ -1194,7 +1187,7 @@ CPUStatePDP11.prototype.trap = function(vector, reason)
this.trapPSW = -1; // reset flag that we have a trap within a trap
/*
* These next properties are purely for bookkeeping purposes; see getTrapStatus()
* These next properties are purely an aid for the Debugger; see getTrapStatus()
*/
this.opFlags |= PDP11.OPFLAG.TRAP;
this.trapVector = vector;
@ -1242,24 +1235,50 @@ CPUStatePDP11.prototype.getTrapStatus = function()
};
/**
* mapUnibus(unibusAddress)
* mapUnibus(addr)
*
* If bits 18-21 of addr are all set (which is implied by addr >= BusPDP11.IOPAGE_UNIBUS aka 0x3C0000),
* then we have a 22-bit address pointing to the top 256Kb range, so if the UNIBUS relocation map is enabled,
* we must pass the lower 18 bits of that address through the map.
*
* Since mapUnibus() only looks at the low 18 bits of addr, there's no need to mask addr first. Note that
* if bits 13-17 are all set, then the 18-bit address points to the top 8Kb of its 256Kb range, and mapUnibus()
* will return addr unchanged, since it should already be pointing to the top 8Kb of the 4Mb 22-bit range.
*
* From the PDP-11/70 Handbook:
*
* On the 11/44 and 11/70, there are a total of 31 mapping registers for address relocation. Each register is
* composed of a double 16-bit PDP-11 word (in consecutive locations) that holds the 22-bit base address. These
* registers have UNIBUS addresses in the range 770200 to 770372.
*
* If the UNIBUS map relocation is not enabled, an incoming 18-bit UNIBUS address has 4 leading zeroes added for
* referencing a 22-bit physical address. The lower 18 bits are the same. No relocation is performed.
*
* If UNIBUS map relocation is enabled, the five high order bits of the UNIBUS address are used to select one of the
* 31 mapping registers. The low-order 13 bits of the incoming address are used as an offset from the base address
* contained in the 22-bit mapping register. To form the physical address, the 13 low-order bits of the UNIBUS
* address are added to 22 bits of the selected mapping register to produce the 22-bit physical address. The lowest
* order bit of all mapping registers is always a zero, since relocation is always on word boundaries.
*
* Sadly, because these mappings occur at a word-granular level, we can't implement the mappings by simply shuffling
* the underlying block around in the Bus component; it would be much more efficient if we could. That's EXACTLY how
* we move the IOPAGE in response to addressing changes. If it turns out that block-granular addresses are commonly
* stored in the unibusMap registers, we could add code to detect that and perform block remapping in those cases.
*
* @this {CPUStatePDP11}
* @param {number} unibusAddress
* @param {number} addr
* @return {number}
*/
CPUStatePDP11.prototype.mapUnibus = function(unibusAddress)
CPUStatePDP11.prototype.mapUnibus = function(addr)
{
var idx = (unibusAddress >> 13) & 0x1f;
var idx = (addr >> 13) & 0x1f;
if (idx < 31) {
if (this.regMMR3 & PDP11.MMR3.UNIBUS_MAP) {
unibusAddress = (this.unibusMap[idx] + (unibusAddress & 0x1ffe)) & 0x3ffffe;
if (unibusAddress >= BusPDP11.IOPAGE_UNIBUS && unibusAddress < BusPDP11.IOPAGE_22BIT) this.panic(898);
addr = (this.unibusMap[idx] + (addr & 0x1ffe)) & 0x3ffffe;
if (addr >= BusPDP11.IOPAGE_UNIBUS && addr < BusPDP11.IOPAGE_22BIT) this.panic(898);
}
} else {
unibusAddress |= BusPDP11.IOPAGE_22BIT;
}
return unibusAddress;
return addr;
};
/**
@ -1284,15 +1303,33 @@ CPUStatePDP11.prototype.mapUnibus = function(unibusAddress)
* it again if all worked. If however something happens to cause a trap then no restore is
* done as setPSW() will have been invoked as part of the trap, which will resynchronize mmuMode.
*
* A PDP 11/70 is different to other PDP 11's in that the highest 18 bit space (017000000 & above)
* A PDP-11/70 is different from other PDP-11s in that the highest 18 bit space (017000000 & above)
* maps directly to UNIBUS space - including low memory. This doesn't appear to be particularly
* useful as it restricts maximum system memory - although it does appear to allow software
* testing of the unibus map. This feature also appears to confuse some OSes which test consecutive
* memory locations to find maximum memory - and on a full memory system find themselves accessing
* testing of the unibus map. This feature also appears to confuse some OSes which test consecutive
* memory locations to find maximum memory -- and on a full memory system find themselves accessing
* low memory again at high addresses.
*
* 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 MMR0
* nonr leng read trap unus unus ena mnt cmp -mode- i/d --page-- enable
* Construction of a Physical Address
* ----------------------------------
*
* Virtual Addr (VA) 12 11 10 9 8 7 6 5 4 3 2 1 0
* Page Addr Field (PAF) 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* + -----------------------------------------------------------------
* Physical Addr (PA) 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
*
* The Page Address Field (PAF) comes from a Page Address Register (PAR) that is selected by Virtual Address (VA)
* bits 15-13. You can see from the above alignments that the VA contributes to the low 13 bits, providing an 8Kb
* range.
*
* VA bits 0-5 pass directly through to the PA; those are also called the DIB (Displacement in Block) bits.
* VA bits 6-12 are added to the low 7 bits of the PAF and are also called the BN (Block Number) bits.
*
* You can also think of the entire PAF as a block number, where each block is 64 bytes. This is consistent with
* the LSIZE register at 177760, which is supposed to contain the number of 64-byte blocks of memory installed.
*
* Note that if a PAR is initialized to zero, successively adding 0200 (0x80) to the PAR will advance the base
* physical address to the next 8Kb page.
*
* @this {CPUStatePDP11}
* @param {number} virtualAddress
@ -1301,15 +1338,16 @@ CPUStatePDP11.prototype.mapUnibus = function(unibusAddress)
*/
CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFlags)
{
var page, pdr, physicalAddress, errorMask = 0;
var page, pdr, physicalAddress;
this.assert(!(virtualAddress & ~0x1ffff) && accessFlags);
/*
* Verify that 1) the incoming virtual address is within the 17-bit I/D range, 2) that
* accessFlags is properly set, and 3) that the MMU is enabled (because non-MMU code paths
* should no longer be going through this function; the Bus component is responsible for
* mapping physical addresses appropriately).
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but *not* the ENABLED bit.
*/
this.assert(!(virtualAddress & ~0x1ffff) && accessFlags && (accessFlags & this.mmuEnable));
if (!(accessFlags & this.mmuEnable)) {
return virtualAddress;
}
this.mmuLastVirtual = virtualAddress;
@ -1318,26 +1356,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
pdr = this.mmuPDR[this.mmuMode][page];
physicalAddress = ((this.mmuPAR[this.mmuMode][page] << 6) + (virtualAddress & 0x1fff)) & this.mmuMask;
if (physicalAddress < this.mmuMemorySize) {
if ((physicalAddress & 1) && !(accessFlags & PDP11.ACCESS.BYTE)) {
this.regErr |= PDP11.CPUERR.ODDADDR;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.ODDMEMADDR);
}
} else {
if (!(this.regMMR3 & 0x10)) {
if (physicalAddress >= BusPDP11.IOPAGE_18BIT) physicalAddress |= BusPDP11.IOPAGE_22BIT;
}
if (physicalAddress < BusPDP11.IOPAGE_22BIT) {
if (physicalAddress >= BusPDP11.IOPAGE_UNIBUS) {
physicalAddress = this.mapUnibus(physicalAddress & 0x3ffff); // 18bit unibus space
}
if (physicalAddress >= this.mmuMemorySize && physicalAddress < BusPDP11.IOPAGE_22BIT) {
this.regErr |= PDP11.CPUERR.NOMEMORY;
this.trap(PDP11.TRAP.BUS_ERROR, PDP11.REASON.NOMEMORY); // KB11-EM does this after ABORT handling - KB11-CM before
}
}
}
var errorMask = 0;
switch (pdr & 0x7) {
case 1: // read-only with trap
errorMask = 0x1000; // MMU trap
@ -1365,18 +1384,19 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
}
if ((pdr & 0x7f08) !== 0x7f00) { // skip checking most common case (hopefully)
if (pdr & 0x8) { // expand downwards
if (pdr & 0x8) { // expand downwards
if (pdr & 0x7f00) {
if ((virtualAddress & 0x1fc0) < ((pdr >> 2) & 0x1fc0)) {
errorMask |= 0x4000; // page length error abort
}
}
} else { // expand upwards
} else { // expand upwards
if ((virtualAddress & 0x1fc0) > ((pdr >> 2) & 0x1fc0)) {
errorMask |= 0x4000; // page length error abort
}
}
}
// aborts and traps: log FIRST trap and MOST RECENT abort
this.mmuPDR[this.mmuMode][page] = pdr;
@ -1384,13 +1404,15 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
this.mmuLastMode = this.mmuMode;
this.mmuLastPage = page;
}
var fTrap = false;
if (errorMask) {
if (errorMask & 0xe000) {
if (this.trapPSW >= 0) errorMask |= 0x80; // Instruction complete
if (!(this.regMMR0 & 0xe000)) {
this.regMMR0 |= errorMask | (this.mmuLastMode << 5) | (this.mmuLastPage << 1);
}
this.trap(PDP11.TRAP.MMU, PDP11.REASON.MAPERROR);
fTrap = true;
}
if (!(this.regMMR0 & 0xf000)) {
//if (physicalAddress < 017772200 || physicalAddress > 017777677) {
@ -1401,6 +1423,9 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
}
}
}
if (fTrap) { // don't trap until the end, because it throws an exception
this.trap(PDP11.TRAP.MMU, PDP11.REASON.MAPERROR);
}
}
return physicalAddress;
};
@ -2017,20 +2042,26 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, src, fnOp)
* @this {CPUStatePDP11}
* @param {number} opCode
* @param {number} data
* @param {number} [writeFlags]
* @param {number} writeFlags (WRITE.BYTE aka 0xff, or WRITE.SBYTE aka 0xffff)
* @return {number}
*/
CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags)
{
this.assert(writeFlags);
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
if (!mode) {
if (!data) {
this.regsGen[reg] &= ~0xff; // TODO: Profile to determine if this is a win
} else if (writeFlags & PDP11.WRITE.SIGNEXT) {
this.regsGen[reg] = ((data << 24) >> 24) & 0xffff;
/*
* Potentially worthless optimization (but it looks good on "paper").
*/
this.regsGen[reg] &= ~writeFlags;
} else {
this.regsGen[reg] = (this.regsGen[reg] & ~0xff) | (data & 0xff);
/*
* Potentially worthwhile optimization: skipping the sign-extending data shifts
* if writeFlags is WRITE.BYTE (but that requires an extra test and separate code paths).
*/
this.regsGen[reg] = (this.regsGen[reg] & ~writeFlags) | (((data << 24) >> 24) & writeFlags);
}
} else {
this.writeByteToPhysical(this.getAddr(mode, reg, PDP11.ACCESS.WRITE_BYTE), data);

View file

@ -43,8 +43,8 @@ if (DEBUGGER) {
var State = require("../../shared/lib/state");
var PDP11 = require("./defines");
var CPUPDP11 = require("./cpu");
var MessagesPDP11 = require("./messages");
var MemoryPDP11 = require("./memory");
var MessagesPDP11 = require("./messages");
}
}
@ -266,6 +266,7 @@ if (DEBUGGER) {
* Register numbers 0-7 are reserved for cpu.regsGen, 8-15 are reserved for cpu.regsAlt, and 16-19 for cpu.regsStack.
*/
DebuggerPDP11.REG_PSW = 20;
DebuggerPDP11.REG_SW = 21;
/*
* Operand type masks; anything that's not covered by OP_SRC or OP_DST must be a OP_OTHER value.
@ -463,8 +464,9 @@ if (DEBUGGER) {
DebuggerPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.cmp = cmp;
this.cpu = cpu;
this.panel = cmp.panel;
/*
* Re-initialize Debugger message support if necessary
@ -675,7 +677,7 @@ if (DEBUGGER) {
this.cpu.setByteDirect(addr, b);
}
if (inc) this.incAddr(dbgAddr, inc);
this.cmp.updateStatus(true); // force a computer status update if, say, video memory was the target
this.cmp.updateDisplays(-1);
}
};
@ -697,7 +699,7 @@ if (DEBUGGER) {
this.cpu.setWordDirect(addr, w);
}
if (inc) this.incAddr(dbgAddr, inc);
this.cmp.updateStatus(true); // force a computer status update if, say, video memory was the target
this.cmp.updateDisplays(-1);
}
};
@ -768,7 +770,7 @@ if (DEBUGGER) {
* done later, by getAddr(), which returns PDP11.ADDR_INVALID for invalid segments, out-of-range offsets,
* etc. The Debugger's low-level get/set memory functions verify all getAddr() results, but even if an
* invalid address is passed through to the Bus memory interfaces, the address will simply be masked with
* BusPDP11.nBusLimit; in the case of PDP11.ADDR_INVALID, that will generally refer to the top of the physical
* bus.nBusMask; in the case of PDP11.ADDR_INVALID, that will generally refer to the top of the physical
* address space.
*
* @this {DebuggerPDP11}
@ -1125,6 +1127,9 @@ if (DEBUGGER) {
case "PC":
iReg = 7;
break;
case "SW":
iReg = 21;
break;
default:
if (sReg.charAt(0) == "R") {
iReg = +sReg.charAt(1);
@ -1139,7 +1144,7 @@ if (DEBUGGER) {
* getRegName(iReg)
*
* @this {DebuggerPDP11}
* @param {number} iReg
* @param {number} iReg (0-7; not used for other registers)
* @return {string}
*/
DebuggerPDP11.prototype.getRegName = function(iReg)
@ -1151,7 +1156,7 @@ if (DEBUGGER) {
* getRegValue(iReg)
*
* Register numbers 0-7 are reserved for cpu.regsGen, 8-15 are reserved for cpu.regsAlt,
* 16-19 for cpu.regsAltStack, and 20 for regPSW.
* 16-19 for cpu.regsAltStack, 20 for regPSW, and 21 for regSW.
*
* @this {DebuggerPDP11}
* @param {number} iReg
@ -1173,6 +1178,9 @@ if (DEBUGGER) {
else if (iReg == DebuggerPDP11.REG_PSW) {
value = this.cpu.getPSW();
}
else if (iReg == DebuggerPDP11.REG_SW && this.panel && this.panel.hasSwitches()) {
value = this.panel.getSW();
}
}
return value;
};
@ -1212,7 +1220,7 @@ if (DEBUGGER) {
if (this.sMessagePrev && sMessage == this.sMessagePrev) return;
this.sMessagePrev = sMessage;
if (this.bitsMessage & MessagesPDP11.HALT) {
if ((this.bitsMessage & MessagesPDP11.HALT) && this.cpu && this.cpu.isRunning()) {
this.stopCPU();
sMessage += " (cpu halted)";
}
@ -1239,7 +1247,7 @@ if (DEBUGGER) {
DebuggerPDP11.prototype.init = function(fAutoStart)
{
this.fInit = true;
this.println("Type ? for help with PDP11 Debugger commands");
this.println("Type ? for help with PDPjs Debugger commands");
this.updateStatus();
if (!fAutoStart) this.setFocus();
if (this.sInitCommands) {
@ -1305,15 +1313,15 @@ if (DEBUGGER) {
};
/**
* stepCPU(nCycles, fRegs, fUpdateStatus)
* stepCPU(nCycles, fRegs, fUpdateDisplays)
*
* @this {DebuggerPDP11}
* @param {number} nCycles (0 for one instruction without checking breakpoints)
* @param {boolean} [fRegs] is true to display registers after step (default is false)
* @param {boolean} [fUpdateStatus] is false to disable Computer status updates (default is true)
* @param {boolean} [fUpdateDisplays] is false to disable Computer display updates (default is true)
* @return {boolean}
*/
DebuggerPDP11.prototype.stepCPU = function(nCycles, fRegs, fUpdateStatus)
DebuggerPDP11.prototype.stepCPU = function(nCycles, fRegs, fUpdateDisplays)
{
if (!this.checkCPU()) return false;
@ -1334,7 +1342,7 @@ if (DEBUGGER) {
nCycles = this.cpu.getBurstCycles(nCycles);
var nCyclesStep = this.cpu.stepCPU(nCycles);
if (nCyclesStep > 0) {
this.cpu.updateTimers(nCycles);
this.cpu.updateTimers(nCyclesStep);
this.nCycles += nCyclesStep;
this.cpu.addCycles(nCyclesStep, true);
this.cpu.updateChecksum(nCyclesStep);
@ -1355,10 +1363,12 @@ if (DEBUGGER) {
/*
* Because we called cpu.stepCPU() and not cpu.startCPU(), we must nudge the Computer's update code,
* and then update our own state. Normally, the only time fUpdateStatus will be false is when doTrace()
* is calling us in a loop, in which case it will perform its own updateStatus() when it's done.
* and then update our own state. Normally, the only time fUpdateDisplays will be false is when doTrace()
* is calling us in a loop, in which case it will perform its own updateDisplays() when it's done.
*/
if (fUpdateStatus !== false) this.cmp.updateStatus();
if (fUpdateDisplays !== false) {
this.cmp.updateDisplays(-1);
}
this.updateStatus(fRegs || false);
return (this.nCycles > 0);
@ -2003,64 +2013,59 @@ if (DEBUGGER) {
if (fTemporary && !dbgAddrBreak.fTemporary) continue;
/*
* We used to calculate the linear address of the breakpoint at the time the
* breakpoint was added, so that a breakpoint set in one mode (eg, in real-mode)
* would still work as intended if the mode changed later (eg, to protected-mode).
*
* However, that created difficulties setting protected-mode breakpoints in segments
* that might not be defined yet, or that could move in physical memory.
*
* If you want to create a real-mode breakpoint that will break regardless of mode,
* use the physical address of the real-mode memory location instead.
* Since we're checking an execution address, which is always virtual, and virtual
* addresses are always restricted to 16 bits, let's mask the breakpoint address to match
* (the user should know better, but we'll be nice).
*/
var addrBreak = this.getAddr(dbgAddrBreak);
var addrBreak = this.getAddr(dbgAddrBreak) & 0xffff;
for (var n = 0; n < nb; n++) {
if (addr + n == addrBreak) {
var a;
fBreak = true;
if (dbgAddrBreak.fTemporary) {
this.findBreakpoint(aBreak, dbgAddrBreak, true, true);
fTemporary = true;
}
if (a = dbgAddrBreak.aCmds) {
/*
* When one or more commands are attached to a breakpoint, we don't halt by default.
* Instead, we set fBreak to true only if, at the completion of all the commands, the
* CPU is halted; in other words, you should include "h" as one of the breakpoint commands
* if you want the breakpoint to stop execution.
*
* Another useful command is "if", which will return false if the expression is false,
* at which point we'll jump ahead to the next "else" command, and if there isn't an "else",
* we abort.
*/
fBreak = false;
for (var j = 0; j < a.length; j++) {
if (!this.doCommand(a[j], true)) {
if (a[j].indexOf("if")) {
fBreak = true; // the failed command wasn't "if", so abort
break;
}
var k = j + 1;
for (; k < a.length; k++) {
if (!a[k].indexOf("else")) break;
j++;
}
if (k == a.length) { // couldn't find an "else" after the "if", so abort
fBreak = true;
break;
}
/*
* If we're still here, we'll execute the "else" command (which is just a no-op),
* followed by any remaining commands.
*/
if ((addr + n) != addrBreak) continue;
var a;
fBreak = true;
if (dbgAddrBreak.fTemporary) {
this.findBreakpoint(aBreak, dbgAddrBreak, true, true);
fTemporary = true;
}
if (a = dbgAddrBreak.aCmds) {
/*
* When one or more commands are attached to a breakpoint, we don't halt by default.
* Instead, we set fBreak to true only if, at the completion of all the commands, the
* CPU is halted; in other words, you should include "h" as one of the breakpoint commands
* if you want the breakpoint to stop execution.
*
* Another useful command is "if", which will return false if the expression is false,
* at which point we'll jump ahead to the next "else" command, and if there isn't an "else",
* we abort.
*/
fBreak = false;
for (var j = 0; j < a.length; j++) {
if (!this.doCommand(a[j], true)) {
if (a[j].indexOf("if")) {
fBreak = true; // the failed command wasn't "if", so abort
break;
}
var k = j + 1;
for (; k < a.length; k++) {
if (!a[k].indexOf("else")) break;
j++;
}
if (k == a.length) { // couldn't find an "else" after the "if", so abort
fBreak = true;
break;
}
/*
* If we're still here, we'll execute the "else" command (which is just a no-op),
* followed by any remaining commands.
*/
}
if (!this.cpu.isRunning()) fBreak = true;
}
if (fBreak) {
if (!fTemporary) this.printBreakpoint(aBreak, i, "hit");
break;
}
if (!this.cpu.isRunning()) fBreak = true;
}
if (fBreak) {
if (!fTemporary) this.printBreakpoint(aBreak, i, "hit");
break;
}
}
}
@ -2384,6 +2389,9 @@ if (DEBUGGER) {
else if (iReg == DebuggerPDP11.REG_PSW) {
sReg = "PS=" + this.toStrBase(cpu.getPSW());
}
else if (iReg == DebuggerPDP11.REG_SW && this.panel && this.panel.hasSwitches()) {
sReg = "SW=" + this.toStrBase(this.panel.getSW(), 3);
}
if (sReg) sReg += ' ';
return sReg;
};
@ -2407,7 +2415,8 @@ if (DEBUGGER) {
sDump += this.getRegOutput(i);
}
sDump += '\n';
sDump += this.getRegOutput(PDP11.REG.SP) + this.getRegOutput(PDP11.REG.PC) + this.getRegOutput(DebuggerPDP11.REG_PSW);
sDump += this.getRegOutput(PDP11.REG.SP) + this.getRegOutput(PDP11.REG.PC);
sDump += this.getRegOutput(DebuggerPDP11.REG_PSW) + this.getRegOutput(DebuggerPDP11.REG_SW);
sDump += this.getFlagOutput('T') + this.getFlagOutput('N') + this.getFlagOutput('Z') + this.getFlagOutput('V') + this.getFlagOutput('C');
return sDump;
};
@ -2711,7 +2720,7 @@ if (DEBUGGER) {
if (asArgs[2] === undefined) {
this.println("begin assemble at " + this.toStrAddr(dbgAddr));
this.fAssemble = true;
this.cmp.updateStatus();
this.cmp.updateDisplays();
return;
}
@ -2849,8 +2858,8 @@ if (DEBUGGER) {
/**
* doDump(asArgs)
*
* The length parameter is interpreted as a number of bytes (or words, or dwords) to dump, and it is
* interpreted using the current base.
* The length parameter is interpreted as a number of bytes (or words, or dwords) to dump,
* and it is interpreted using the current base.
*
* @this {DebuggerPDP11}
* @param {Array.<string>} asArgs (formerly sCmd, [sAddr], [sLen] and [sBytes])
@ -2923,7 +2932,7 @@ if (DEBUGGER) {
return;
}
}
if (!sAddr) sCmd = this.sCmdDumpPrev || "db";
if (!sAddr) sCmd = this.sCmdDumpPrev || "dw";
} else {
this.sCmdDumpPrev = sCmd;
}
@ -2945,17 +2954,20 @@ if (DEBUGGER) {
if (len > 0x10000) len = 0x10000; // prevent bad user (or variable) input from producing excessive output
}
var sDump = "";
var size = (sCmd == "dd"? 4 : (sCmd == "dw"? 2 : 1));
/*
* I've changed the code below to effectively make "dw" the default if only "d" is specified,
* since this is primarily a word-oriented machine.
*/
var size = (sCmd == "dd"? 4 : (sCmd == "db"? 1 : 2));
var nBytes = (size * len) || 128;
var nLines = ((nBytes + 15) >> 4) || 1;
var sDump = "";
while (nLines-- && nBytes > 0) {
var data = 0, shift = 0, i;
var sData = "", sChars = "";
sAddr = this.toStrAddr(dbgAddr);
/*
* Dump 8 bytes per line when using base 8, and dump 16 bytes when using base 16 (or when dumping dwords).
* Dump 8 bytes per line when using base 8, and dump 16 bytes when using base 16.
*
* And while we used to always call getByte() and assemble them into words or dwords as appropriate, I've
* changed the logic below to honor "dw" by calling getWord(), since the Bus interfaces have been updated
@ -2963,8 +2975,10 @@ if (DEBUGGER) {
*
* Besides, it's nice for "db" and "dw" to generate the same Bus activity that typical byte and word reads do.
*/
for (i = (size == 4? 16 : this.nBase); i > 0 && nBytes > 0; i--) {
var n = 1;
var i, n;
var data = 0, shift = 0;
for (i = this.nBase; i > 0 && nBytes > 0; i -= n, nBytes -= n) {
n = 1;
var v = size == 1? this.getByte(dbgAddr, n) : this.getWord(dbgAddr, (n = 2));
data |= (v << (shift << 3));
shift += n;
@ -2974,7 +2988,6 @@ if (DEBUGGER) {
data = shift = 0;
}
sChars += (v >= 32 && v < 128? String.fromCharCode(v) : '.');
nBytes -= n;
}
if (sDump) sDump += '\n';
sDump += sAddr + " " + sData + ((i == 0)? (' ' + sChars) : "");
@ -3027,8 +3040,7 @@ if (DEBUGGER) {
if (vNew & ~mask) {
this.println("warning: " + str.toHex(vNew) + " exceeds " + size + "-byte value");
}
var vOld = fnGet.call(this, dbgAddr);
this.println("changing " + this.toStrAddr(dbgAddr) + " from " + this.toStrBase(vOld, size) + " to " + this.toStrBase(vNew, size));
this.println("changing " + this.toStrAddr(dbgAddr) + (this.messageEnabled(MessagesPDP11.BUS)? "" : (" from " + this.toStrBase(fnGet.call(this, dbgAddr), size))) + " to " + this.toStrBase(vNew, size));
fnSet.call(this, dbgAddr, vNew, size);
}
};
@ -3400,6 +3412,12 @@ if (DEBUGGER) {
case "C":
if (w) cpu.setCF(); else cpu.clearCF();
break;
case "SW":
if (this.panel && this.panel.hasSwitches()) {
this.panel.setSW(w);
break;
}
/* falls through */
default:
if (sRegMatch.charAt(0) == 'R') {
var iReg = +sRegMatch.charAt(1);
@ -3411,7 +3429,7 @@ if (DEBUGGER) {
this.println("unknown register: " + sReg);
return;
}
this.cmp.updateStatus();
this.cmp.updateDisplays();
this.println("updated registers:");
}
@ -3655,12 +3673,13 @@ if (DEBUGGER) {
},
function onCountStepComplete() {
/*
* We explicitly called stepCPU() with fUpdateStatus === false, because repeatedly
* calling updateStatus() can be very slow, especially if a Control Panel is present
* with displayLiveRegs enabled, so once the repeat count has been exhausted, we must
* perform a final updateStatus().
* We explicitly called stepCPU() with fUpdateDisplays set to false, because repeatedly
* calling updateDisplays() can be very slow, especially if a Control Panel is present with
* displayLiveRegs enabled, so once the repeat count has been exhausted, we must perform
* a final updateDisplays().
*/
dbg.cmp.updateStatus();
if (dbg.panel && dbg.panel.stop) dbg.panel.stop();
dbg.cmp.updateDisplays();
dbg.setBusy(false);
}
);

View file

@ -296,11 +296,13 @@ var PDP11 = {
DSPACE: 0x10000 // getVirtualByMode() sets bit 17 in any 16-bit virtual address that refers to D space (as opposed to I space)
},
/*
* Internal flags passed to writeByteByMode(), etc.
* Internal flags passed to writeDstByte()
*
* The BYTE and SBYTE values have been chosen so that they can be used directly as masks.
*/
WRITE: {
NORMAL: 0x0, // write byte or word normally
SIGNEXT: 0x1 // sign-extend a byte to a word
BYTE: 0xff, // write byte normally
SBYTE: 0xffff // sign-extend byte to word
},
CPUERR: {
RED: 0x0004, // red zone stack limit
@ -316,7 +318,7 @@ var PDP11 = {
PAGE_D: 0x0010, // last fault occurred in D space
PAGE_MODE: 0x0060, // processor mode as of last fault
COMPLETED: 0x0080, // last instruction completed
DSTMODE: 0x0100, // only destination mode references will be relocated (for diagnostic use)
DSTMODE: 0x0100, // only destination mode references will be relocated (aka MAINT bit)
MMU_TRAPS: 0x0200, // enable MMU traps
UNUSED: 0x0C00,
TRAP_MMU: 0x1000, // trap: MMU
@ -364,73 +366,80 @@ var PDP11 = {
* For more details: https://github.com/google/closure-compiler/wiki/ECMAScript6
*/
UNIBUS: { //16-bit 18-bit 22-bit Hex Description
SISDR0: 0o172200, // Supervisor I Space Descriptor Register 0
SISDR1: 0o172202, // Supervisor I Space Descriptor Register 1
SISDR2: 0o172204, // Supervisor I Space Descriptor Register 2
SISDR3: 0o172206, // Supervisor I Space Descriptor Register 3
SISDR4: 0o172210, // Supervisor I Space Descriptor Register 4
SISDR5: 0o172212, // Supervisor I Space Descriptor Register 5
SISDR6: 0o172214, // Supervisor I Space Descriptor Register 6
SISDR7: 0o172216, // Supervisor I Space Descriptor Register 7
SDSDR0: 0o172220, // Supervisor D Space Descriptor Register 0
SDSDR1: 0o172222, // Supervisor D Space Descriptor Register 1
SDSDR2: 0o172224, // Supervisor D Space Descriptor Register 2
SDSDR3: 0o172226, // Supervisor D Space Descriptor Register 3
SDSDR4: 0o172230, // Supervisor D Space Descriptor Register 4
SDSDR5: 0o172232, // Supervisor D Space Descriptor Register 5
SDSDR6: 0o172234, // Supervisor D Space Descriptor Register 6
SDSDR7: 0o172236, // Supervisor D Space Descriptor Register 7
SISAR0: 0o172240, // Supervisor I Space Address Register 0
SISAR1: 0o172242, // Supervisor I Space Address Register 1
SISAR2: 0o172244, // Supervisor I Space Address Register 2
SISAR3: 0o172246, // Supervisor I Space Address Register 3
SISAR4: 0o172250, // Supervisor I Space Address Register 4
SISAR5: 0o172252, // Supervisor I Space Address Register 5
SISAR6: 0o172254, // Supervisor I Space Address Register 6
SISAR7: 0o172256, // Supervisor I Space Address Register 7
SDSAR0: 0o172260, // Supervisor D Space Address Register 0
SDSAR1: 0o172262, // Supervisor D Space Address Register 1
SDSAR2: 0o172264, // Supervisor D Space Address Register 2
SDSAR3: 0o172266, // Supervisor D Space Address Register 3
SDSAR4: 0o172270, // Supervisor D Space Address Register 4
SDSAR5: 0o172272, // Supervisor D Space Address Register 5
SDSAR6: 0o172274, // Supervisor D Space Address Register 6
SDSAR7: 0o172276, // Supervisor D Space Address Register 7
KISDR0: 0o172300, // Kernel I Space Descriptor Register 0
KISDR1: 0o172302, // Kernel I Space Descriptor Register 1
KISDR2: 0o172304, // Kernel I Space Descriptor Register 2
KISDR3: 0o172306, // Kernel I Space Descriptor Register 3
KISDR4: 0o172310, // Kernel I Space Descriptor Register 4
KISDR5: 0o172312, // Kernel I Space Descriptor Register 5
KISDR6: 0o172314, // Kernel I Space Descriptor Register 6
KISDR7: 0o172316, // Kernel I Space Descriptor Register 7
KDSDR0: 0o172320, // Kernel D Space Descriptor Register 0
KDSDR1: 0o172322, // Kernel D Space Descriptor Register 1
KDSDR2: 0o172324, // Kernel D Space Descriptor Register 2
KDSDR3: 0o172326, // Kernel D Space Descriptor Register 3
KDSDR4: 0o172330, // Kernel D Space Descriptor Register 4
KDSDR5: 0o172332, // Kernel D Space Descriptor Register 5
KDSDR6: 0o172334, // Kernel D Space Descriptor Register 6
KDSDR7: 0o172336, // Kernel D Space Descriptor Register 7
KISAR0: 0o172340, // Kernel I Space Address Register 0
KISAR1: 0o172342, // Kernel I Space Address Register 1
KISAR2: 0o172344, // Kernel I Space Address Register 2
KISAR3: 0o172346, // Kernel I Space Address Register 3
KISAR4: 0o172350, // Kernel I Space Address Register 4
KISAR5: 0o172352, // Kernel I Space Address Register 5
KISAR6: 0o172354, // Kernel I Space Address Register 6
KISAR7: 0o172356, // Kernel I Space Address Register 7
KDSAR0: 0o172360, // Kernel D Space Address Register 0
KDSAR1: 0o172362, // Kernel D Space Address Register 1
KDSAR2: 0o172364, // Kernel D Space Address Register 2
KDSAR3: 0o172366, // Kernel D Space Address Register 3
KDSAR4: 0o172370, // Kernel D Space Address Register 4
KDSAR5: 0o172372, // Kernel D Space Address Register 5
KDSAR6: 0o172374, // Kernel D Space Address Register 6
KDSAR7: 0o172376, // Kernel D Space Address Register 7
UNIMAP: 0o170200, // UNIBUS Mapping Registers (0-31) 64 words (ends at 0o170372)
SIPDR0: 0o172200, // Supervisor I Page Descriptor Register 0
SIPDR1: 0o172202, // Supervisor I Page Descriptor Register 1
SIPDR2: 0o172204, // Supervisor I Page Descriptor Register 2
SIPDR3: 0o172206, // Supervisor I Page Descriptor Register 3
SIPDR4: 0o172210, // Supervisor I Page Descriptor Register 4
SIPDR5: 0o172212, // Supervisor I Page Descriptor Register 5
SIPDR6: 0o172214, // Supervisor I Page Descriptor Register 6
SIPDR7: 0o172216, // Supervisor I Page Descriptor Register 7
SDPDR0: 0o172220, // Supervisor D Page Descriptor Register 0
SDPDR1: 0o172222, // Supervisor D Page Descriptor Register 1
SDPDR2: 0o172224, // Supervisor D Page Descriptor Register 2
SDPDR3: 0o172226, // Supervisor D Page Descriptor Register 3
SDPDR4: 0o172230, // Supervisor D Page Descriptor Register 4
SDPDR5: 0o172232, // Supervisor D Page Descriptor Register 5
SDPDR6: 0o172234, // Supervisor D Page Descriptor Register 6
SDPDR7: 0o172236, // Supervisor D Page Descriptor Register 7
SIPAR0: 0o172240, // Supervisor I Page Address Register 0
SIPAR1: 0o172242, // Supervisor I Page Address Register 1
SIPAR2: 0o172244, // Supervisor I Page Address Register 2
SIPAR3: 0o172246, // Supervisor I Page Address Register 3
SIPAR4: 0o172250, // Supervisor I Page Address Register 4
SIPAR5: 0o172252, // Supervisor I Page Address Register 5
SIPAR6: 0o172254, // Supervisor I Page Address Register 6
SIPAR7: 0o172256, // Supervisor I Page Address Register 7
SDPAR0: 0o172260, // Supervisor D Page Address Register 0
SDPAR1: 0o172262, // Supervisor D Page Address Register 1
SDPAR2: 0o172264, // Supervisor D Page Address Register 2
SDPAR3: 0o172266, // Supervisor D Page Address Register 3
SDPAR4: 0o172270, // Supervisor D Page Address Register 4
SDPAR5: 0o172272, // Supervisor D Page Address Register 5
SDPAR6: 0o172274, // Supervisor D Page Address Register 6
SDPAR7: 0o172276, // Supervisor D Page Address Register 7
KIPDR0: 0o172300, // Kernel I Page Descriptor Register 0
KIPDR1: 0o172302, // Kernel I Page Descriptor Register 1
KIPDR2: 0o172304, // Kernel I Page Descriptor Register 2
KIPDR3: 0o172306, // Kernel I Page Descriptor Register 3
KIPDR4: 0o172310, // Kernel I Page Descriptor Register 4
KIPDR5: 0o172312, // Kernel I Page Descriptor Register 5
KIPDR6: 0o172314, // Kernel I Page Descriptor Register 6
KIPDR7: 0o172316, // Kernel I Page Descriptor Register 7
KDPDR0: 0o172320, // Kernel D Page Descriptor Register 0
KDPDR1: 0o172322, // Kernel D Page Descriptor Register 1
KDPDR2: 0o172324, // Kernel D Page Descriptor Register 2
KDPDR3: 0o172326, // Kernel D Page Descriptor Register 3
KDPDR4: 0o172330, // Kernel D Page Descriptor Register 4
KDPDR5: 0o172332, // Kernel D Page Descriptor Register 5
KDPDR6: 0o172334, // Kernel D Page Descriptor Register 6
KDPDR7: 0o172336, // Kernel D Page Descriptor Register 7
KIPAR0: 0o172340, // Kernel I Page Address Register 0
KIPAR1: 0o172342, // Kernel I Page Address Register 1
KIPAR2: 0o172344, // Kernel I Page Address Register 2
KIPAR3: 0o172346, // Kernel I Page Address Register 3
KIPAR4: 0o172350, // Kernel I Page Address Register 4
KIPAR5: 0o172352, // Kernel I Page Address Register 5
KIPAR6: 0o172354, // Kernel I Page Address Register 6
KIPAR7: 0o172356, // Kernel I Page Address Register 7
KDPAR0: 0o172360, // Kernel D Page Address Register 0
KDPAR1: 0o172362, // Kernel D Page Address Register 1
KDPAR2: 0o172364, // Kernel D Page Address Register 2
KDPAR3: 0o172366, // Kernel D Page Address Register 3
KDPAR4: 0o172370, // Kernel D Page Address Register 4
KDPAR5: 0o172372, // Kernel D Page Address Register 5
KDPAR6: 0o172374, // Kernel D Page Address Register 6
KDPAR7: 0o172376, // Kernel D Page Address Register 7
MMR3: 0o172516, // 772516 17772516
RLCS: 0o174400, // RL11 Control Status Register
RLBA: 0o174402, // RL11 Bus Address Register
RLDA: 0o174404, // RL11 Disk Address Register
RLMP: 0o174406, // RL11 Multi-Purpose Register
RLBE: 0o174410, // RL11 Bus (Address) Extension Register (RLV12 controller only)
LKS: 0o177546, // KW11-L Clock Status
PRS: 0o177550, // PC11 (and PR11) Reader Status Register
@ -443,44 +452,44 @@ var PDP11 = {
XCSR: 0o177564, // Display Terminal: Transmitter Status Register
XBUF: 0o177566, // Display Terminal: Transmitter Data Buffer Register
CNSL: 0o177570, // Console Switch and Front Panel Display
CNSW: 0o177570, // Console (Front Panel) Switch Register
MMR0: 0o177572, // 777572 17777572
MMR1: 0o177574, // 777574 17777574
MMR2: 0o177576, // 777576 17777576
UISDR0: 0o177600, // User I Space Descriptor Register 0
UISDR1: 0o177602, // User I Space Descriptor Register 1
UISDR2: 0o177604, // User I Space Descriptor Register 2
UISDR3: 0o177606, // User I Space Descriptor Register 3
UISDR4: 0o177610, // User I Space Descriptor Register 4
UISDR5: 0o177612, // User I Space Descriptor Register 5
UISDR6: 0o177614, // User I Space Descriptor Register 6
UISDR7: 0o177616, // User I Space Descriptor Register 7
UDSDR0: 0o177620, // User D Space Descriptor Register 0
UDSDR1: 0o177622, // User D Space Descriptor Register 1
UDSDR2: 0o177624, // User D Space Descriptor Register 2
UDSDR3: 0o177626, // User D Space Descriptor Register 3
UDSDR4: 0o177630, // User D Space Descriptor Register 4
UDSDR5: 0o177632, // User D Space Descriptor Register 5
UDSDR6: 0o177634, // User D Space Descriptor Register 6
UDSDR7: 0o177636, // User D Space Descriptor Register 7
UISAR0: 0o177640, // User I Space Address Register 0
UISAR1: 0o177642, // User I Space Address Register 1
UISAR2: 0o177644, // User I Space Address Register 2
UISAR3: 0o177646, // User I Space Address Register 3
UISAR4: 0o177650, // User I Space Address Register 4
UISAR5: 0o177652, // User I Space Address Register 5
UISAR6: 0o177654, // User I Space Address Register 6
UISAR7: 0o177656, // User I Space Address Register 7
UDSAR0: 0o177660, // User D Space Address Register 0
UDSAR1: 0o177662, // User D Space Address Register 1
UDSAR2: 0o177664, // User D Space Address Register 2
UDSAR3: 0o177666, // User D Space Address Register 3
UDSAR4: 0o177670, // User D Space Address Register 4
UDSAR5: 0o177672, // User D Space Address Register 5
UDSAR6: 0o177674, // User D Space Address Register 6
UDSAR7: 0o177676, // User D Space Address Register 7
UIPDR0: 0o177600, // User I Page Descriptor Register 0
UIPDR1: 0o177602, // User I Page Descriptor Register 1
UIPDR2: 0o177604, // User I Page Descriptor Register 2
UIPDR3: 0o177606, // User I Page Descriptor Register 3
UIPDR4: 0o177610, // User I Page Descriptor Register 4
UIPDR5: 0o177612, // User I Page Descriptor Register 5
UIPDR6: 0o177614, // User I Page Descriptor Register 6
UIPDR7: 0o177616, // User I Page Descriptor Register 7
UDPDR0: 0o177620, // User D Page Descriptor Register 0
UDPDR1: 0o177622, // User D Page Descriptor Register 1
UDPDR2: 0o177624, // User D Page Descriptor Register 2
UDPDR3: 0o177626, // User D Page Descriptor Register 3
UDPDR4: 0o177630, // User D Page Descriptor Register 4
UDPDR5: 0o177632, // User D Page Descriptor Register 5
UDPDR6: 0o177634, // User D Page Descriptor Register 6
UDPDR7: 0o177636, // User D Page Descriptor Register 7
UIPAR0: 0o177640, // User I Page Address Register 0
UIPAR1: 0o177642, // User I Page Address Register 1
UIPAR2: 0o177644, // User I Page Address Register 2
UIPAR3: 0o177646, // User I Page Address Register 3
UIPAR4: 0o177650, // User I Page Address Register 4
UIPAR5: 0o177652, // User I Page Address Register 5
UIPAR6: 0o177654, // User I Page Address Register 6
UIPAR7: 0o177656, // User I Page Address Register 7
UDPAR0: 0o177660, // User D Page Address Register 0
UDPAR1: 0o177662, // User D Page Address Register 1
UDPAR2: 0o177664, // User D Page Address Register 2
UDPAR3: 0o177666, // User D Page Address Register 3
UDPAR4: 0o177670, // User D Page Address Register 4
UDPAR5: 0o177672, // User D Page Address Register 5
UDPAR6: 0o177674, // User D Page Address Register 6
UDPAR7: 0o177676, // User D Page Address Register 7
R0SET0: 0o177700,
R1SET0: 0o177701,
@ -500,8 +509,10 @@ var PDP11 = {
R6USER: 0o177717,
/*
* This next group of registers is largely ignored; all accesses are routed to regsControl[]
* This next group of registers is largely ignored; all accesses are routed to regsControl[],
* and therefore are managed as a block of 8 "CTRL" registers.
*/
CTRL: 0o177740,
LAERR: 0o177740, // Low Address Error (11/70 only)
HAERR: 0o177742, // High Address Error (11/70 only)
MEMERR: 0o177744, // Memory System Error (11/70 only)
@ -511,7 +522,7 @@ var PDP11 = {
UNDEF1: 0o177754,
UNDEF2: 0o177756,
LSIZE: 0o177760, // Lower Size Register (last 32-word block) (11/70 only)
LSIZE: 0o177760, // Lower Size Register (last 64-byte block #) (11/70 only)
HSIZE: 0o177762, // Upper Size Register (always zero) (11/70 only)
SYSID: 0o177764, // System ID Register (11/70 only)
CPUERR: 0o177766, // CPU error (11/70 only)
@ -520,31 +531,6 @@ var PDP11 = {
SL: 0o177774, // Stack Limit Register
PSW: 0o177776 // 777776 17777776 0x3FFFFE Processor Status Word
},
PC11: { // High Speed Reader & Punch (PR11 is a Reader-only unit)
PRI: 4, // NOTE: reader has precedence over punch
RVEC: 0o70, // reader vector
PVEC: 0o74, // punch vector
PRS: {
RE: 0x0001, // Reader Enable (W/O)
RIE: 0x0040, // Reader Interrupt Enable (allows the DONE and ERROR bits to trigger an interrupt)
DONE: 0x0080, // Done (R/O)
BUSY: 0x0800, // Busy (R/O)
ERROR: 0x8000, // Error (R/O)
CLEAR: 0x08C0, // bits cleared on INIT
RMASK: 0xFFFE, // bits readable (TODO: All I know for sure is that bit 0 is NOT readable; see readPRS())
WMASK: 0x0041, // bits writable
BAUD: 3600
},
PRB: {
MASK: 0x00FF // Data
},
PPS: {
/*
* TODO: Flesh this out if/when we add Paper Tape Punch support
*/
BAUD: 600
},
},
DL11: { // Serial Line Interface (program compatible with the KL11 for control of console teleprinters)
PRI: 4,
RVEC: 0o60,
@ -587,7 +573,7 @@ var PDP11 = {
DATA: 0x00FF // Transmitted Data (W/O) TODO: Determine why pdp11.js effectively defined this as 0x7F
}
},
KW11: { // KW11-L Line Time Clock
KW11: { // KW11-L Line Time Clock (60Hz; well, OK, or 50Hz, if you're in the UK, I suppose...)
PRI: 6,
VEC: 0o100,
DELAY: 0,
@ -595,6 +581,126 @@ var PDP11 = {
IE: 0x0040, // Interrupt Enable
MON: 0x0080 // Monitor
}
},
PC11: { // High Speed Reader & Punch (PR11 is a Reader-only unit)
PRI: 4, // NOTE: reader has precedence over punch
RVEC: 0o70, // reader vector
PVEC: 0o74, // punch vector
PRS: {
RE: 0x0001, // Reader Enable (W/O)
RIE: 0x0040, // Reader Interrupt Enable (allows the DONE and ERROR bits to trigger an interrupt)
DONE: 0x0080, // Done (R/O)
BUSY: 0x0800, // Busy (R/O)
ERROR: 0x8000, // Error (R/O)
CLEAR: 0x08C0, // bits cleared on INIT
RMASK: 0xFFFE, // bits readable (TODO: All I know for sure is that bit 0 is NOT readable; see readPRS())
WMASK: 0x0041, // bits writable
BAUD: 3600
},
PRB: {
MASK: 0x00FF // Data
},
PPS: {
/*
* TODO: Flesh this out if/when we add Paper Tape Punch support
*/
BAUD: 600
},
},
RL11: { // RL11 Disk Controller
PRI: 5,
VEC: 0o160,
RLCS: { // Control Status Register (174400)
DRDY: 0x0001, // Drive Ready (R/O)
FUNC: 0x000E, // Function Code (F2,F1,F0) (R/W)
BAE: 0x0030, // Bus Address Extension bits (BA17,BA16) (R/W)
IE: 0x0040, // Interrupt Enable (R/W)
CRDY: 0x0080, // Controller Ready (R/W)
DS: 0x0300, // Drive Select (DS1,DS0) (R/W)
ERRC: 0x3C00, // Error Code (R/O)
DE: 0x4000, // Drive Error (R/O)
ERR: 0x8000, // Composite Error (R/O)
CLEAR: 0x3F7E, // bits cleared on INIT (bits 1-6 and 8-13 are cleared)
SET: 0x0080, // bits set on INIT (bit 7 is set)
RMASK: 0xFFFF, // no write-only bits
WMASK: 0x03FE, // bits writable
SHIFT: {
FUNC: 1,
DS: 8
}
},
RLBA: { // Bus Address Register (174402)
WMASK: 0xFFFE // bit 0 is effectively not writable (always zero)
},
/*
* This register has 3 formats: one for Seek, another for Read/Write, and a third for Get Status
*/
RLDA: { // Disk Address Register (174404)
SEEK_CMD: 0x0001, // Seek: bit 0 must be set, bits 1 and 3 must be clear
SEEK_DIR: 0x0004, // Direction (clear to move heads away from spindle (lower cylinder), set to move to higher cylinder)
SEEK_HS: 0x0010, // Head Select (clear to select upper head, set to select lower head)
SEEK_CAD: 0xFF80, // Cylinder Address Difference
RW_SA: 0x003F, // Sector Address
RW_HS: 0x0040, // Head Select
RW_CA: 0xFF80, // Cylinder Address (RL01 has 256 cylinders, RL02 has 512)
GS_CMD: 0x0003, // Get Status: bit 0 must be set, bit 1 set, and bits 2 and 4-7 clear (bits 8-15 unused)
GS_RST: 0x0008, // Reset (when set, clears error register before sending status word to controller)
SHIFT: {
RW_HS: 6,
RW_CA: 7
}
},
/*
* This register has 3 formats: one for Read Header, another for Read/Write, and a third for Get Status
*/
RLMP: { // Multi-Purpose Register (177406)
GS_ST: { // Major State Code (of the drive)
LOADC: 0x0, // Load Cartridge
SPINUP: 0x1, // Spin-Up
BRUSHC: 0x2, // Brush Cycle
LOADH: 0x3, // Load Heads
SEEK: 0x4, // Seek
LOCKON: 0x5, // Lock On
UNLOADH:0x6, // Unload Heads
SPINDN: 0x7 // Spin-Down
},
GS_BH: 0x0008, // Brushes Home
GS_HO: 0x0010, // Heads Out
GS_CO: 0x0020, // Cover Open (or dust cover is not in place)
GS_HS: 0x0040, // Head Selected (0 for upper head, 1 for lower head)
GS_DT: 0x0080, // Drive Type (0 for RL01, 1 for RL02)
GS_DSE: 0x0100, // Drive Select Error
GS_VC: 0x0200, // Volume Check (Set during transition from a head load state to a head-on-track state; cleared by execution of a Get Status command with Bit 3 asserted)
GS_WGE: 0x0400, // Write Gate Error
GS_SPE: 0x0800, // Spin Error
GS_SKTO: 0x1000, // Seek Time-Out
GS_WL: 0x2000, // Write Lock
GS_CHE: 0x4000, // Current Head Error
GS_WDE: 0x8000 // Write Data Error
},
RLBE: { // Bus (Address) Extension Register (174410)
MASK: 0x003F // bits 5-0 correspond to bus address bits 21-16
},
ERRC: { // NOTE: These error codes are pre-shifted to read/write directly from/to RLCS.ERRC
OPI: 0x0400, // Operation Incomplete
DCRC: 0x0800, // Read Data CRC
WCE: 0x0800, // Write Check Error
HCRC: 0x0C00, // Header CRC
DLT: 0x1000, // Data Late
HNF: 0x1400, // Header Not Found
NXM: 0x2000, // Non-Existent Memory
MPE: 0x2400 // Memory Parity Error (RLV12 only)
},
FUNC: { // NOTE: These function codes are pre-shifted to read/write directly from/to RLCS.FUNC
NOP: 0b0000, // No-Op
WCHK: 0b0010, // Write Check
STATUS: 0b0100, // Get Status
SEEK: 0b0110, // Seek
RHDR: 0b1000, // Read Header
WDATA: 0b1010, // Write Data
RDATA: 0b1100, // Read Data
RDNC: 0b1110 // Read Data without Header Check
}
}
};

View file

@ -38,8 +38,10 @@ if (NODE) {
var Component = require("../../shared/lib/component");
var State = require("../../shared/lib/state");
var BusPDP11 = require("./bus");
var MemoryPDP11 = require("./memory");
var MessagesPDP11 = require("./messages");
var PC11 = require("./pc11");
var RL11 = require("./rl11");
}
/**
@ -48,7 +50,6 @@ if (NODE) {
* The Device component implements the following "default" devices:
*
* KW11 (KW11-L Line Time Clock)
* CNSL (Console Switch and Front Panel Display)
*
* as well providing access to all the MMU and CPU registers, PSW, etc.
*
@ -60,14 +61,7 @@ function DevicePDP11(parmsDevice)
{
Component.call(this, "Device", parmsDevice, DevicePDP11, MessagesPDP11.DEVICE);
this.console = { // CNSL registers
data: 0,
address: 0,
misc: 0x14,
switches: 0
};
this.kw11 = { // LW11 registers
this.kw11 = { // KW11 registers
csr: 0,
timer: -1 // initBus() will initialize this timer ID
};
@ -122,6 +116,7 @@ DevicePDP11.M9312 = [
DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cmp = cmp;
this.cpu = cpu;
this.dbg = dbg;
@ -145,7 +140,6 @@ DevicePDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
DevicePDP11.prototype.reset = function()
{
this.console.misc = (this.console.misc & ~0x77) | 0x14; // kernel 16 bit
this.kw11.lks = 0;
};
@ -161,6 +155,7 @@ DevicePDP11.prototype.kw11_interrupt = function()
this.cpu.setTrigger(this.kw11.trigger);
this.cpu.setTimer(this.kw11.timer, 1000/60);
}
if (this.cmp) this.cmp.updateDisplays(1);
};
/**
@ -193,33 +188,6 @@ DevicePDP11.prototype.writeLKS = function(data, addr)
this.kw11.lks = data & ~PDP11.KW11.LKS.MON;
};
/**
* readCNSL(addr)
*
* If addr is set, then this a normal read, so we should return normal results (ie, switches);
* if addr is NOT set, then this is a read-before-write, so we must return the value being updated (ie, data).
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.CNSL or 177570)
* @return {number}
*/
DevicePDP11.prototype.readCNSL = function(addr)
{
return (addr? this.console.switches : this.console.data) & 0xffff;
};
/**
* writeCNSL(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.CNSL or 177570)
*/
DevicePDP11.prototype.writeCNSL = function(data, addr)
{
this.console.data = data;
};
/**
* readMMR0(addr)
*
@ -242,7 +210,6 @@ DevicePDP11.prototype.readMMR0 = function(addr)
DevicePDP11.prototype.writeMMR0 = function(data, addr)
{
this.cpu.setMMR0(data);
this.updateConsoleMode();
};
/**
@ -303,96 +270,116 @@ DevicePDP11.prototype.readMMR3 = function(addr)
DevicePDP11.prototype.writeMMR3 = function(data, addr)
{
this.cpu.setMMR3(data);
this.updateConsoleMode();
};
/**
* updateConsoleMode()
* readUNIMAP(addr)
*
* NOTE: The UNIBUS map is 32 registers spread across 64 words, so we first calculate the word index.
*
* @this {DevicePDP11}
*/
DevicePDP11.prototype.updateConsoleMode = function()
{
/*
* Set bit to 1 (22-bit), 2 (18-bit), or 4 (16-bit)
*/
var bit = this.cpu.mmuEnable? ((this.cpu.regMMR3 & PDP11.MMR3.MMU_22BIT)? 1 : 2) : 4;
this.console.misc = (this.console.misc & ~7) | bit;
};
/**
* readSISDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.SISDR0--SISDR7 or 172200--172216)
* @param {number} addr (eg, PDP11.UNIBUS.UNIMAP)
* @return {number}
*/
DevicePDP11.prototype.readSISDR = function(addr)
DevicePDP11.prototype.readUNIMAP = function(addr)
{
var word = (addr >> 1) & 0x3f, reg = word >> 1;
var data = this.cpu.unibusMap[reg];
return (word & 1)? (data >> 16) : (data & 0xffff);
};
/**
* writeUNIMAP(data, addr)
*
* NOTE: The UNIBUS map is 32 registers spread across 64 words, so we first calculate the word index.
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.UNIMAP)
*/
DevicePDP11.prototype.writeUNIMAP = function(data, addr)
{
var word = (addr >> 1) & 0x3f, reg = word >> 1;
if (word & 1) {
this.cpu.unibusMap[reg] = (this.cpu.unibusMap[reg] & 0xffff) | ((data & 0x003f) << 16);
} else {
this.cpu.unibusMap[reg] = (this.cpu.unibusMap[reg] & ~0xffff) | (data & 0xfffe);
}
};
/**
* readSIPDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.SIPDR0--SIPDR7 or 172200--172216)
* @return {number}
*/
DevicePDP11.prototype.readSIPDR = function(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPDR[1][reg];
};
/**
* writeSISDR(data, addr)
* writeSIPDR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.SISDR0--SISDR7 or 172200--172216)
* @param {number} addr (eg, PDP11.UNIBUS.SIPDR0--SIPDR7 or 172200--172216)
*/
DevicePDP11.prototype.writeSISDR = function(data, addr)
DevicePDP11.prototype.writeSIPDR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[1][reg] = data & 0xff0f;
};
/**
* readSDSDR(addr)
* readSDPDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.SDSDR0--SDSDR7 or 172220--172236)
* @param {number} addr (eg, PDP11.UNIBUS.SDPDR0--SDPDR7 or 172220--172236)
* @return {number}
*/
DevicePDP11.prototype.readSDSDR = function(addr)
DevicePDP11.prototype.readSDPDR = function(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPDR[1][reg];
};
/**
* writeSDSDR(data, addr)
* writeSDPDR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.SDSDR0--SDSDR7 or 172220--172236)
* @param {number} addr (eg, PDP11.UNIBUS.SDPDR0--SDPDR7 or 172220--172236)
*/
DevicePDP11.prototype.writeSDSDR = function(data, addr)
DevicePDP11.prototype.writeSDPDR = function(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPDR[1][reg] = data & 0xff0f;
};
/**
* readSISAR(addr)
* readSIPAR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.SISAR0--SISAR7 or 172240--172256)
* @param {number} addr (eg, PDP11.UNIBUS.SIPAR0--SIPAR7 or 172240--172256)
* @return {number}
*/
DevicePDP11.prototype.readSISAR = function(addr)
DevicePDP11.prototype.readSIPAR = function(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPAR[1][reg];
};
/**
* writeSISAR(data, addr)
* writeSIPAR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.SISAR0--SISAR7 or 172240--172256)
* @param {number} addr (eg, PDP11.UNIBUS.SIPAR0--SIPAR7 or 172240--172256)
*/
DevicePDP11.prototype.writeSISAR = function(data, addr)
DevicePDP11.prototype.writeSIPAR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPAR[1][reg] = data;
@ -401,26 +388,26 @@ DevicePDP11.prototype.writeSISAR = function(data, addr)
};
/**
* readSDSAR(addr)
* readSDPAR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.SDSAR0--SDSAR7 or 172260--172276)
* @param {number} addr (eg, PDP11.UNIBUS.SDPAR0--SDPAR7 or 172260--172276)
* @return {number}
*/
DevicePDP11.prototype.readSDSAR = function(addr)
DevicePDP11.prototype.readSDPAR = function(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPAR[1][reg];
};
/**
* writeSDSAR(data, addr)
* writeSDPAR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.SDSAR0--SDSAR7 or 172260--172276)
* @param {number} addr (eg, PDP11.UNIBUS.SDPAR0--SDPAR7 or 172260--172276)
*/
DevicePDP11.prototype.writeSDSAR = function(data, addr)
DevicePDP11.prototype.writeSDPAR = function(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPAR[1][reg] = data;
@ -428,78 +415,78 @@ DevicePDP11.prototype.writeSDSAR = function(data, addr)
};
/**
* readKISDR(addr)
* readKIPDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.KISDR0--KISDR7 or 172300--172316)
* @param {number} addr (eg, PDP11.UNIBUS.KIPDR0--KIPDR7 or 172300--172316)
* @return {number}
*/
DevicePDP11.prototype.readKISDR = function(addr)
DevicePDP11.prototype.readKIPDR = function(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPDR[0][reg];
};
/**
* writeKISDR(data, addr)
* writeKIPDR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.KISDR0--KISDR7 or 172300--172316)
* @param {number} addr (eg, PDP11.UNIBUS.KIPDR0--KIPDR7 or 172300--172316)
*/
DevicePDP11.prototype.writeKISDR = function(data, addr)
DevicePDP11.prototype.writeKIPDR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[0][reg] = data & 0xff0f;
};
/**
* readKDSDR(addr)
* readKDPDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.KDSDR0--KDSDR7 or 172320--172336)
* @param {number} addr (eg, PDP11.UNIBUS.KDPDR0--KDPDR7 or 172320--172336)
* @return {number}
*/
DevicePDP11.prototype.readKDSDR = function(addr)
DevicePDP11.prototype.readKDPDR = function(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPDR[0][reg];
};
/**
* writeKDSDR(data, addr)
* writeKDPDR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.KDSDR0--KDSDR7 or 172320--172336)
* @param {number} addr (eg, PDP11.UNIBUS.KDPDR0--KDPDR7 or 172320--172336)
*/
DevicePDP11.prototype.writeKDSDR = function(data, addr)
DevicePDP11.prototype.writeKDPDR = function(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPDR[0][reg] = data & 0xff0f;
};
/**
* readKISAR(addr)
* readKIPAR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.KISAR0--KISAR7 or 172340--172356)
* @param {number} addr (eg, PDP11.UNIBUS.KIPAR0--KIPAR7 or 172340--172356)
* @return {number}
*/
DevicePDP11.prototype.readKISAR = function(addr)
DevicePDP11.prototype.readKIPAR = function(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPAR[0][reg];
};
/**
* writeKISAR(data, addr)
* writeKIPAR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.KISAR0--KISAR7 or 172340--172356)
* @param {number} addr (eg, PDP11.UNIBUS.KIPAR0--KIPAR7 or 172340--172356)
*/
DevicePDP11.prototype.writeKISAR = function(data, addr)
DevicePDP11.prototype.writeKIPAR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPAR[0][reg] = data;
@ -508,26 +495,26 @@ DevicePDP11.prototype.writeKISAR = function(data, addr)
};
/**
* readKDSAR(addr)
* readKDPAR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.KDSAR0--KDSAR7 or 172360--172376)
* @param {number} addr (eg, PDP11.UNIBUS.KDPAR0--KDPAR7 or 172360--172376)
* @return {number}
*/
DevicePDP11.prototype.readKDSAR = function(addr)
DevicePDP11.prototype.readKDPAR = function(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPAR[0][reg];
};
/**
* writeKDSAR(data, addr)
* writeKDPAR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.KDSAR0--KDSAR7 or 172360--172376)
* @param {number} addr (eg, PDP11.UNIBUS.KDPAR0--KDPAR7 or 172360--172376)
*/
DevicePDP11.prototype.writeKDSAR = function(data, addr)
DevicePDP11.prototype.writeKDPAR = function(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPAR[0][reg] = data;
@ -535,78 +522,78 @@ DevicePDP11.prototype.writeKDSAR = function(data, addr)
};
/**
* readUISDR(addr)
* readUIPDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.UISDR0--UISDR7 or 177600--177616)
* @param {number} addr (eg, PDP11.UNIBUS.UIPDR0--UIPDR7 or 177600--177616)
* @return {number}
*/
DevicePDP11.prototype.readUISDR = function(addr)
DevicePDP11.prototype.readUIPDR = function(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPDR[3][reg];
};
/**
* writeUISDR(data, addr)
* writeUIPDR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.UISDR0--UISDR7 or 177600--177616)
* @param {number} addr (eg, PDP11.UNIBUS.UIPDR0--UIPDR7 or 177600--177616)
*/
DevicePDP11.prototype.writeUISDR = function(data, addr)
DevicePDP11.prototype.writeUIPDR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPDR[3][reg] = data & 0xff0f;
};
/**
* readUDSDR(addr)
* readUDPDR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.UDSDR0--UDSDR7 or 177620--177636)
* @param {number} addr (eg, PDP11.UNIBUS.UDPDR0--UDPDR7 or 177620--177636)
* @return {number}
*/
DevicePDP11.prototype.readUDSDR = function(addr)
DevicePDP11.prototype.readUDPDR = function(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPDR[3][reg];
};
/**
* writeUDSDR(data, addr)
* writeUDPDR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.UDSDR0--UDSDR7 or 177620--177636)
* @param {number} addr (eg, PDP11.UNIBUS.UDPDR0--UDPDR7 or 177620--177636)
*/
DevicePDP11.prototype.writeUDSDR = function(data, addr)
DevicePDP11.prototype.writeUDPDR = function(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPDR[3][reg] = data & 0xff0f;
};
/**
* readUISAR(addr)
* readUIPAR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.UISAR0--UISAR7 or 177640--177656)
* @param {number} addr (eg, PDP11.UNIBUS.UIPAR0--UIPAR7 or 177640--177656)
* @return {number}
*/
DevicePDP11.prototype.readUISAR = function(addr)
DevicePDP11.prototype.readUIPAR = function(addr)
{
var reg = (addr >> 1) & 7;
return this.cpu.mmuPAR[3][reg];
};
/**
* writeUISAR(data, addr)
* writeUIPAR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.UISAR0--UISAR7 or 177640--177656)
* @param {number} addr (eg, PDP11.UNIBUS.UIPAR0--UIPAR7 or 177640--177656)
*/
DevicePDP11.prototype.writeUISAR = function(data, addr)
DevicePDP11.prototype.writeUIPAR = function(data, addr)
{
var reg = (addr >> 1) & 7;
this.cpu.mmuPAR[3][reg] = data;
@ -615,26 +602,26 @@ DevicePDP11.prototype.writeUISAR = function(data, addr)
};
/**
* readUDSAR(addr)
* readUDPAR(addr)
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.UDSAR0--UDSAR7 or 177660--177676)
* @param {number} addr (eg, PDP11.UNIBUS.UDPAR0--UDPAR7 or 177660--177676)
* @return {number}
*/
DevicePDP11.prototype.readUDSAR = function(addr)
DevicePDP11.prototype.readUDPAR = function(addr)
{
var reg = ((addr >> 1) & 7) + 8;
return this.cpu.mmuPAR[3][reg];
};
/**
* writeUDSAR(data, addr)
* writeUDPAR(data, addr)
*
* @this {DevicePDP11}
* @param {number} data
* @param {number} addr (eg, PDP11.UNIBUS.UDSAR0--UDSAR7 or 177660--177676)
* @param {number} addr (eg, PDP11.UNIBUS.UDPAR0--UDPAR7 or 177660--177676)
*/
DevicePDP11.prototype.writeUDSAR = function(data, addr)
DevicePDP11.prototype.writeUDPAR = function(data, addr)
{
var reg = ((addr >> 1) & 7) + 8;
this.cpu.mmuPAR[3][reg] = data;
@ -848,7 +835,7 @@ DevicePDP11.prototype.writeR6USER = function(data, addr)
*/
DevicePDP11.prototype.readCTRL = function(addr)
{
var reg = (addr - PDP11.UNIBUS.LAERR) >> 1;
var reg = (addr - PDP11.UNIBUS.CTRL) >> 1;
return this.cpu.regsControl[reg];
};
@ -861,20 +848,36 @@ DevicePDP11.prototype.readCTRL = function(addr)
*/
DevicePDP11.prototype.writeCTRL = function(data, addr)
{
var reg = (addr - PDP11.UNIBUS.LAERR) >> 1;
var reg = (addr - PDP11.UNIBUS.CTRL) >> 1;
this.cpu.regsControl[reg] = data;
};
/**
* readSIZE(addr)
*
* We're adhering to DEC's documentation, which says:
*
* This read-only register specifies the memory size of the system. It is defined to indicate the
* last addressable block of 32 words in memory (bit 0 is equivalent to bit 6 of the Physical Address).
*
* Looking at the Memory Clear "toggle-in" code in /devices/pdp11/machine/1170/panel/debugger/README.md, the
* memory loop gives up when the block number stored in KIPAR0 is >= LSIZE, suggesting that LSIZE is actually
* the total number of 64-byte blocks, rather than the block number of the last block. But that code is
* not conclusive, since it writes 8192 bytes at a time rather than 64, so it doesn't really matter if LSIZE
* is off by one.
*
* @this {DevicePDP11}
* @param {number} addr (eg, PDP11.UNIBUS.LSIZE--HSIZE or 177760--177762)
* @return {number}
*/
DevicePDP11.prototype.readSIZE = function(addr)
{
return addr == PDP11.UNIBUS.LSIZE? ((BusPDP11.MAX_MEMORY >> 6) - 1) : 0;
/*
* TODO: getMemorySize() returns an aggregate total, so if there are multiple discontiguous
* chunks of RAM, this could return the wrong result; another interface, getHighestAddress(),
* might be required.
*/
return addr == PDP11.UNIBUS.LSIZE? ((this.bus.getMemorySize(MemoryPDP11.TYPE.RAM) >> 6) - 1) : 0;
};
/**
@ -1058,24 +1061,24 @@ DevicePDP11.prototype.writeIgnored = function(data, addr)
* ES6 ALERT: As you can see below, I've finally started using computed property names.
*/
DevicePDP11.UNIBUS_IOTABLE = {
[PDP11.UNIBUS.SISDR0]: /* 172200 */ [null, null, DevicePDP11.prototype.readSISDR, DevicePDP11.prototype.writeSISDR, "SISDR", PDP11.MODEL_1145],
[PDP11.UNIBUS.SDSDR0]: /* 172220 */ [null, null, DevicePDP11.prototype.readSDSDR, DevicePDP11.prototype.writeSDSDR, "SDSDR", PDP11.MODEL_1145],
[PDP11.UNIBUS.SISAR0]: /* 172240 */ [null, null, DevicePDP11.prototype.readSISAR, DevicePDP11.prototype.writeSISAR, "SISAR", PDP11.MODEL_1145],
[PDP11.UNIBUS.SDSAR0]: /* 172260 */ [null, null, DevicePDP11.prototype.readSDSAR, DevicePDP11.prototype.writeSDSAR, "SDSAR", PDP11.MODEL_1145],
[PDP11.UNIBUS.KISDR0]: /* 172300 */ [null, null, DevicePDP11.prototype.readKISDR, DevicePDP11.prototype.writeKISDR, "KISDR", PDP11.MODEL_1145],
[PDP11.UNIBUS.KDSDR0]: /* 172320 */ [null, null, DevicePDP11.prototype.readKDSDR, DevicePDP11.prototype.writeKDSDR, "KDSDR", PDP11.MODEL_1145],
[PDP11.UNIBUS.KISAR0]: /* 172340 */ [null, null, DevicePDP11.prototype.readKISAR, DevicePDP11.prototype.writeKISAR, "KISAR", PDP11.MODEL_1145],
[PDP11.UNIBUS.KDSAR0]: /* 172360 */ [null, null, DevicePDP11.prototype.readKDSAR, DevicePDP11.prototype.writeKDSAR, "KDSAR", PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR3]: /* 172516 */ [null, null, DevicePDP11.prototype.readMMR3, DevicePDP11.prototype.writeMMR3, "MMR3", PDP11.MODEL_1145],
[PDP11.UNIBUS.UNIMAP]: /* 170200 */ [null, null, DevicePDP11.prototype.readUNIMAP, DevicePDP11.prototype.writeUNIMAP, "UNIMAP", 64, PDP11.MODEL_1170],
[PDP11.UNIBUS.SIPDR0]: /* 172200 */ [null, null, DevicePDP11.prototype.readSIPDR, DevicePDP11.prototype.writeSIPDR, "SIPDR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.SDPDR0]: /* 172220 */ [null, null, DevicePDP11.prototype.readSDPDR, DevicePDP11.prototype.writeSDPDR, "SDPDR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.SIPAR0]: /* 172240 */ [null, null, DevicePDP11.prototype.readSIPAR, DevicePDP11.prototype.writeSIPAR, "SIPAR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.SDPAR0]: /* 172260 */ [null, null, DevicePDP11.prototype.readSDPAR, DevicePDP11.prototype.writeSDPAR, "SDPAR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.KIPDR0]: /* 172300 */ [null, null, DevicePDP11.prototype.readKIPDR, DevicePDP11.prototype.writeKIPDR, "KIPDR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.KDPDR0]: /* 172320 */ [null, null, DevicePDP11.prototype.readKDPDR, DevicePDP11.prototype.writeKDPDR, "KDPDR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.KIPAR0]: /* 172340 */ [null, null, DevicePDP11.prototype.readKIPAR, DevicePDP11.prototype.writeKIPAR, "KIPAR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.KDPAR0]: /* 172360 */ [null, null, DevicePDP11.prototype.readKDPAR, DevicePDP11.prototype.writeKDPAR, "KDPAR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR3]: /* 172516 */ [null, null, DevicePDP11.prototype.readMMR3, DevicePDP11.prototype.writeMMR3, "MMR3", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.LKS]: /* 177546 */ [null, null, DevicePDP11.prototype.readLKS, DevicePDP11.prototype.writeLKS, "LKS"],
[PDP11.UNIBUS.CNSL]: /* 177570 */ [null, null, DevicePDP11.prototype.readCNSL, DevicePDP11.prototype.writeCNSL, "CNSL"],
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0, "MMR0", PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR1]: /* 177574 */ [null, null, DevicePDP11.prototype.readMMR1, DevicePDP11.prototype.writeIgnored, "MMR1", PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR2]: /* 177576 */ [null, null, DevicePDP11.prototype.readMMR2, DevicePDP11.prototype.writeIgnored, "MMR2", PDP11.MODEL_1145],
[PDP11.UNIBUS.UISDR0]: /* 177600 */ [null, null, DevicePDP11.prototype.readUISDR, DevicePDP11.prototype.writeUISDR, "UISDR", PDP11.MODEL_1145],
[PDP11.UNIBUS.UDSDR0]: /* 177620 */ [null, null, DevicePDP11.prototype.readUDSDR, DevicePDP11.prototype.writeUDSDR, "UDSDR", PDP11.MODEL_1145],
[PDP11.UNIBUS.UISAR0]: /* 177640 */ [null, null, DevicePDP11.prototype.readUISAR, DevicePDP11.prototype.writeUISAR, "UISAR", PDP11.MODEL_1145],
[PDP11.UNIBUS.UDSAR0]: /* 177660 */ [null, null, DevicePDP11.prototype.readUDSAR, DevicePDP11.prototype.writeUDSAR, "UDSAR", PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0, "MMR0", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR1]: /* 177574 */ [null, null, DevicePDP11.prototype.readMMR1, DevicePDP11.prototype.writeIgnored, "MMR1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.MMR2]: /* 177576 */ [null, null, DevicePDP11.prototype.readMMR2, DevicePDP11.prototype.writeIgnored, "MMR2", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.UIPDR0]: /* 177600 */ [null, null, DevicePDP11.prototype.readUIPDR, DevicePDP11.prototype.writeUIPDR, "UIPDR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.UDPDR0]: /* 177620 */ [null, null, DevicePDP11.prototype.readUDPDR, DevicePDP11.prototype.writeUDPDR, "UDPDR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.UIPAR0]: /* 177640 */ [null, null, DevicePDP11.prototype.readUIPAR, DevicePDP11.prototype.writeUIPAR, "UIPAR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.UDPAR0]: /* 177660 */ [null, null, DevicePDP11.prototype.readUDPAR, DevicePDP11.prototype.writeUDPAR, "UDPAR", 8, PDP11.MODEL_1145],
[PDP11.UNIBUS.R0SET0]: /* 177700 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R0SET0"],
[PDP11.UNIBUS.R1SET0]: /* 177701 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R1SET0"],
[PDP11.UNIBUS.R2SET0]: /* 177702 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R2SET0"],
@ -1084,129 +1087,25 @@ DevicePDP11.UNIBUS_IOTABLE = {
[PDP11.UNIBUS.R5SET0]: /* 177705 */ [null, null, DevicePDP11.prototype.readRSET0, DevicePDP11.prototype.writeRSET0, "R5SET0"],
[PDP11.UNIBUS.R6KERNEL]:/* 177706 */ [null, null, DevicePDP11.prototype.readR6KERNEL,DevicePDP11.prototype.writeR6KERNEL,"R6KERNEL"],
[PDP11.UNIBUS.R7KERNEL]:/* 177707 */ [null, null, DevicePDP11.prototype.readR7KERNEL,DevicePDP11.prototype.writeR7KERNEL,"R7KERNEL"],
[PDP11.UNIBUS.R0SET1]: /* 177710 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R0SET1", PDP11.MODEL_1145],
[PDP11.UNIBUS.R1SET1]: /* 177711 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R1SET1", PDP11.MODEL_1145],
[PDP11.UNIBUS.R2SET1]: /* 177712 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R2SET1", PDP11.MODEL_1145],
[PDP11.UNIBUS.R3SET1]: /* 177713 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R3SET1", PDP11.MODEL_1145],
[PDP11.UNIBUS.R4SET1]: /* 177714 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R4SET1", PDP11.MODEL_1145],
[PDP11.UNIBUS.R5SET1]: /* 177715 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R5SET1", PDP11.MODEL_1145],
[PDP11.UNIBUS.R6SUPER]: /* 177716 */ [null, null, DevicePDP11.prototype.readR6SUPER, DevicePDP11.prototype.writeR6SUPER, "R6SUPER", PDP11.MODEL_1145],
[PDP11.UNIBUS.R6USER]: /* 177717 */ [null, null, DevicePDP11.prototype.readR6USER, DevicePDP11.prototype.writeR6USER, "R6USER", PDP11.MODEL_1145],
[PDP11.UNIBUS.LAERR]: /* 177740 */ [null, null, DevicePDP11.prototype.readCTRL, DevicePDP11.prototype.writeCTRL, "CTRL", PDP11.MODEL_1170],
[PDP11.UNIBUS.LSIZE]: /* 177760 */ [null, null, DevicePDP11.prototype.readSIZE, DevicePDP11.prototype.writeSIZE, "LSIZE", PDP11.MODEL_1170],
[PDP11.UNIBUS.HSIZE]: /* 177762 */ [null, null, DevicePDP11.prototype.readSIZE, DevicePDP11.prototype.writeSIZE, "HSIZE", PDP11.MODEL_1170],
[PDP11.UNIBUS.SYSID]: /* 177764 */ [null, null, DevicePDP11.prototype.readSYSID, DevicePDP11.prototype.writeSYSID, "SYSID", PDP11.MODEL_1170],
[PDP11.UNIBUS.CPUERR]: /* 177766 */ [null, null, DevicePDP11.prototype.readCPUERR, DevicePDP11.prototype.writeCPUERR, "CPUERR", PDP11.MODEL_1170],
[PDP11.UNIBUS.MB]: /* 177770 */ [null, null, DevicePDP11.prototype.readMB, DevicePDP11.prototype.writeMB, "MB", PDP11.MODEL_1170],
[PDP11.UNIBUS.R0SET1]: /* 177710 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R0SET1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R1SET1]: /* 177711 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R1SET1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R2SET1]: /* 177712 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R2SET1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R3SET1]: /* 177713 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R3SET1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R4SET1]: /* 177714 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R4SET1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R5SET1]: /* 177715 */ [null, null, DevicePDP11.prototype.readRSET1, DevicePDP11.prototype.writeRSET1, "R5SET1", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R6SUPER]: /* 177716 */ [null, null, DevicePDP11.prototype.readR6SUPER, DevicePDP11.prototype.writeR6SUPER, "R6SUPER", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.R6USER]: /* 177717 */ [null, null, DevicePDP11.prototype.readR6USER, DevicePDP11.prototype.writeR6USER, "R6USER", 1, PDP11.MODEL_1145],
[PDP11.UNIBUS.CTRL]: /* 177740 */ [null, null, DevicePDP11.prototype.readCTRL, DevicePDP11.prototype.writeCTRL, "CTRL", 8, PDP11.MODEL_1170],
[PDP11.UNIBUS.LSIZE]: /* 177760 */ [null, null, DevicePDP11.prototype.readSIZE, DevicePDP11.prototype.writeSIZE, "LSIZE", 1, PDP11.MODEL_1170],
[PDP11.UNIBUS.HSIZE]: /* 177762 */ [null, null, DevicePDP11.prototype.readSIZE, DevicePDP11.prototype.writeSIZE, "HSIZE", 1, PDP11.MODEL_1170],
[PDP11.UNIBUS.SYSID]: /* 177764 */ [null, null, DevicePDP11.prototype.readSYSID, DevicePDP11.prototype.writeSYSID, "SYSID", 1, PDP11.MODEL_1170],
[PDP11.UNIBUS.CPUERR]: /* 177766 */ [null, null, DevicePDP11.prototype.readCPUERR, DevicePDP11.prototype.writeCPUERR, "CPUERR", 1, PDP11.MODEL_1170],
[PDP11.UNIBUS.MB]: /* 177770 */ [null, null, DevicePDP11.prototype.readMB, DevicePDP11.prototype.writeMB, "MB", 1, PDP11.MODEL_1170],
[PDP11.UNIBUS.PIR]: /* 177772 */ [null, null, DevicePDP11.prototype.readPIR, DevicePDP11.prototype.writePIR, "PIR"],
[PDP11.UNIBUS.SL]: /* 177774 */ [null, null, DevicePDP11.prototype.readSL, DevicePDP11.prototype.writeSL, "SL"],
[PDP11.UNIBUS.PSW]: /* 177776 */ [null, null, DevicePDP11.prototype.readPSW, DevicePDP11.prototype.writePSW, "PSW"]
};
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.SDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.KDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSDR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UISAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR3] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR4] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR5] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR6] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR7] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UDSAR0];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.HAERR] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.MEMERR] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.CACHEC] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.MAINT] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.HITMISS]= DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UNDEF1] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.UNDEF2] = DevicePDP11.UNIBUS_IOTABLE[PDP11.UNIBUS.LAERR];
/**
* DevicePDP11.init()
*
@ -1231,6 +1130,10 @@ DevicePDP11.init = function()
device = new PC11(parmsDevice);
Component.bindComponentControls(device, eDevice, PDP11.APPCLASS);
break;
case 'rl11':
device = new RL11(parmsDevice);
Component.bindComponentControls(device, eDevice, PDP11.APPCLASS);
break;
}
}
};

1233
modules/pdp11/lib/disk.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -174,10 +174,9 @@ function MemoryPDP11(bus, addr, used, size, type, controller)
a = this.ab = new Array(this.size);
} else {
/*
* NOTE: This is the default mode of operation (!TYPEDARRAYS && !BYTEARRAYS), because it
* seems to provide the best performance; and although in theory, that performance might
* come at twice the overhead of TYPEDARRAYS, it's increasingly likely that the JavaScript
* runtime will notice that all we ever store are 32-bit values, and optimize accordingly.
* NOTE: This used to be the default mode of operation (!TYPEDARRAYS && !BYTEARRAYS), because
* it seemed to provide the best performance; however, that was then, and this is now. TYPEDARRAYS
* is more efficient.
*/
a = this.adw = new Array(this.size >> 2);
}
@ -386,28 +385,30 @@ MemoryPDP11.prototype = {
return false;
},
/**
* zero(off, len)
* zero(off, len, pattern)
*
* Zeros the block. Supporting off and len parameters is probably overkill, and makes more
* work in the non-TYPEDARRAY, non-BYTEARRAY case, because there all we have is an array of DWORDs,
* but that's not the typical case.
* work in the non-TYPEDARRAY, non-BYTEARRAY case, but that's not the typical case. The other
* exception is controller-based blocks, which may not have any array backing at all.
*
* @this {MemoryPDP11}
* @param {number} [off] (optional starting byte offset within block)
* @param {number} [len] (optional maximum number of bytes; default is the entire block)
* @param {number} [pattern]
*/
zero: function(off, len) {
zero: function(off, len, pattern) {
var i;
off = off || 0;
pattern &= 0xff;
/*
* NOTE: If len happens to be larger than the block, that's OK, because we also bounds-check the index.
*/
if (len === undefined) len = this.size;
Component.assert(off >= 0 && off < this.size);
if (TYPEDARRAYS || BYTEARRAYS) {
for (i = off; len-- && i < this.ab.length; i++) this.ab[i] = 0;
if ((TYPEDARRAYS || BYTEARRAYS) && this.ab) {
for (i = off; len-- && i < this.ab.length; i++) this.ab[i] = pattern;
} else {
for (i = off; len-- && i < this.size; i++) this.writeByteDirect(off, 0, this.addr + off);
for (i = off; len-- && i < this.size; i++) this.writeByteDirect(off, pattern, this.addr + off);
}
},
/**
@ -575,7 +576,9 @@ MemoryPDP11.prototype = {
*
* TODO: Determine if we should have separate readByteNone(), readWordNone() and readLongNone() functions
* to return 0xff, 0xffff and 0xffffffff|0, respectively. This seems sufficient for now, as it seems unlikely
* that a system would require nonexistent memory locations to return ALL bits set.
* that a system would require nonexistent memory locations to return ALL bits set. However, another factor
* is whether or not ODDADDR faults take precedence over NOMEMORY faults; if they do, then we need separate
* interfaces.
*
* Also, I'm reluctant to address that potential issue by simply returning -1, because to date, the above
* Memory interfaces have always returned values that are properly masked to 8, 16 or 32 bits, respectively.
@ -588,9 +591,8 @@ MemoryPDP11.prototype = {
readNone: function readNone(off, addr) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEMORY) /* && !off */) {
this.dbg.printMessage("attempt to read invalid address " + this.dbg.toStrBase(addr), true);
this.dbg.stopInstruction();
}
this.bus.fault(addr, PDP11.ACCESS.READ);
this.bus.fault(addr, PDP11.CPUERR.NOMEMORY, PDP11.ACCESS.READ);
return 0xff;
},
/**
@ -604,9 +606,8 @@ MemoryPDP11.prototype = {
writeNone: function writeNone(off, v, addr) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEMORY) /* && !off */) {
this.dbg.printMessage("attempt to write " + this.dbg.toStrBase(v) + " to invalid addresses " + this.dbg.toStrBase(addr), true);
this.dbg.stopInstruction();
}
this.bus.fault(addr, PDP11.ACCESS.WRITE);
this.bus.fault(addr, PDP11.CPUERR.NOMEMORY, PDP11.ACCESS.WRITE);
},
/**
* readWordDefault(off, addr)
@ -655,7 +656,7 @@ MemoryPDP11.prototype = {
*/
readWordMemory: function readWordMemory(off, addr) {
if (PDP11.MEMFAULT && (off & 0x1)) {
this.bus.fault(addr, PDP11.ACCESS.READ_WORD);
this.bus.fault(addr, PDP11.CPUERR.ODDADDR, PDP11.ACCESS.READ_WORD);
}
if (BYTEARRAYS) {
return this.ab[off] | (this.ab[off + 1] << 8);
@ -699,7 +700,7 @@ MemoryPDP11.prototype = {
*/
writeWordMemory: function writeWordMemory(off, w, addr) {
if (PDP11.MEMFAULT && (off & 0x1)) {
this.bus.fault(addr, PDP11.ACCESS.WRITE_WORD);
this.bus.fault(addr, PDP11.CPUERR.ODDADDR, PDP11.ACCESS.WRITE_WORD);
}
if (BYTEARRAYS) {
this.ab[off] = (w & 0xff);
@ -809,7 +810,7 @@ MemoryPDP11.prototype = {
*/
readWordBE: function readWordBE(off, addr) {
if (PDP11.MEMFAULT && (off & 0x1)) {
this.bus.fault(addr, PDP11.ACCESS.READ_WORD);
this.bus.fault(addr, PDP11.CPUERR.ODDADDR, PDP11.ACCESS.READ_WORD);
}
return this.dv.getUint16(off, true);
},
@ -824,7 +825,7 @@ MemoryPDP11.prototype = {
readWordLE: function readWordLE(off, addr) {
var w;
if (PDP11.MEMFAULT && (off & 0x1)) {
this.bus.fault(addr, PDP11.ACCESS.READ_WORD);
this.bus.fault(addr, PDP11.CPUERR.ODDADDR, PDP11.ACCESS.READ_WORD);
}
/*
* TODO: For non-WORDBUS machines, it remains to be seen if there's any advantage to checking the offset
@ -877,7 +878,7 @@ MemoryPDP11.prototype = {
*/
writeWordBE: function writeWordBE(off, w, addr) {
if (PDP11.MEMFAULT && (off & 0x1)) {
this.bus.fault(addr, PDP11.ACCESS.WRITE_WORD);
this.bus.fault(addr, PDP11.CPUERR.ODDADDR, PDP11.ACCESS.WRITE_WORD);
}
this.dv.setUint16(off, w, true);
this.fDirty = true;
@ -892,7 +893,7 @@ MemoryPDP11.prototype = {
*/
writeWordLE: function writeWordLE(off, w, addr) {
if (PDP11.MEMFAULT && (off & 0x1)) {
this.bus.fault(addr, PDP11.ACCESS.WRITE_WORD);
this.bus.fault(addr, PDP11.CPUERR.ODDADDR, PDP11.ACCESS.WRITE_WORD);
}
/*
* TODO: For non-WORDBUS machines, it remains to be seen if there's any advantage to checking the offset

View file

@ -35,9 +35,11 @@
var MessagesPDP11 = {
CPU: 0x00000001,
TRAP: 0x00000010,
FAULT: 0x00000020,
BUS: 0x00000040,
MEMORY: 0x00000080,
DEVICE: 0x00000100,
ROM: 0x00000100,
DEVICE: 0x00000200,
KEYBOARD: 0x00010000,
KEYS: 0x00020000,
DISK: 0x00200000,
@ -67,8 +69,10 @@ var MessagesPDP11 = {
MessagesPDP11.CATEGORIES = {
"cpu": MessagesPDP11.CPU,
"trap": MessagesPDP11.TRAP,
"fault": MessagesPDP11.FAULT,
"bus": MessagesPDP11.BUS,
"memory": MessagesPDP11.MEMORY,
"rom": MessagesPDP11.ROM,
"device": MessagesPDP11.DEVICE,
"keyboard": MessagesPDP11.KEYBOARD, // "kbd" is also allowed as shorthand for "keyboard"; see doMessages()
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects

View file

@ -54,19 +54,176 @@ if (NODE) {
function PanelPDP11(parmsPanel)
{
Component.call(this, "Panel", parmsPanel, PanelPDP11);
/*
* If there are any live registers, LEDs, etc, to display, this will provide a count.
* TODO: Add some UI for fDisplayLiveRegs (either an XML property, or a UI checkbox, or both).
*/
this.cLiveRegs = 0;
/*
* TODO: Add some UI for displayLiveRegs (either an XML property, or a UI checkbox, or both)
*/
this.flags.displayLiveRegs = true;
this.nPeriodicCount = 0;
this.nPeriodicLimit = 60;
this.fDisplayLiveRegs = true;
/*
* regSwitches contains the Front Panel (aka Console) 'SWITCH' register, which is also available
* as a read-only register at 177570 (but only the low 16 bits).
*
* regAddr is an internal register containing the contents of the Front Panel's 'ADDRESS' display,
* and regData corresponds to the 'DATA' display. They are updated by setAddr() and setData(),
* which in turn take care of calling setLEDArray().
*
* The state of ALL switches is maintained in this.switches, and likewise all LED states are
* maintained in this.leds, but for convenience, we also mirror some of those states in dedicated
* variables (eg, regSwitches for the 'SWITCH' register, fLEDTest for the 'TEST' switch, etc).
*/
this.regSwitches = 0;
this.regAddr = this.regData = 0;
/*
* The panel hardware has the following additional (supported) state; note that there are several
* settings on a real Front Panel that we don't support (eg, stepping one cycle vs. one instruction).
*
* While my initial intent is to eventually support all the ADDRSEL switch settings, I probably
* won't bother with any DATASEL switch settings; instead, I will automatically display the data
* register (regData) [the equivalent of selecting 'DISPLAY REGISTER'] except when data is being
* examined or deposited [the equivalent of selecting 'DATA PATHS'].
*/
this.fLEDTest = false; // LED (lamp) test in progress
this.fExamine = false; // true if the previously pressed switch was the 'EXAM' switch
this.fDeposit = false; // true if the previously pressed switch was the 'DEP' switch
this.nAddrSel = PanelPDP11.ADDRSEL.CONS_PHY;
/*
* Every LED has a simple numeric value, assigned when setBinding() is called:
*
* zero if "off", non-zero if "on"
*
* initBus() will call displayLEDs() to ensure that every LED is set to its initial value.
*/
this.leds = {};
/*
* Every switch has an array associated with it:
*
* [0]: initial value of switch (0 if "down", 1 if "up")
* [1]: current value of switch
* [2]: true if the switch is momentary, false if not
* [3]: true if the switch is currently pressed, false if released
* [4]: optional handler to call whenever the switch is pressed or released
* [5]: optional switch index (used with CNSW switches 'S0' through 'S21')
*
* initBus() will call displaySwitches() to ensure that every switch is the position represented below.
*
* NOTE: Not all switches have the same "process" criteria. For example, 'TEST' will perform a LED test
* when it is momentarily pressed "up", whereas 'LOAD [ADRS]' will load the 'ADDRESS' register from the
* 'SWITCH' register when it is momentarily pressed "down".
*
* This means that processLEDTest(value) must act when value == 1 ("up"), whereas processLoadAddr(value)
* must act when value == 0 ("down"). You can infer all this from the table below, because the initial value
* of any momentary switch is its "inactive" value, so the opposite is its "active" value.
*/
this.switches = {
'START': [1, 1, true, false, this.processStart],
'STEP': [1, 1, false, false, this.processStep],
'ENABLE': [1, 1, false, false, this.processEnable],
'CONT': [1, 1, true, false, this.processContinue],
'DEP': [0, 0, true, false, this.processDeposit],
'EXAM': [1, 1, true, false, this.processExamine],
'LOAD': [1, 1, true, false, this.processLoadAddr],
'TEST': [0, 0, true, false, this.processLEDTest]
};
for (var i = 0; i < 22; i++) {
this.switches['S'+i] = [0, 0, false, false, this.processSwitchReg, i];
}
}
Component.subclass(PanelPDP11);
PanelPDP11.ADDRSEL = {
KERNEL_I: 0, // use a 16-bit virtual address where bits 16 to 21 are always OFF
KERNEL_D: 1, // use a 16-bit virtual address where bits 16 to 21 are always OFF
SUPER_I: 2, // use a 16-bit virtual address where bits 16 to 21 are always OFF
SUPER_D: 3, // use a 16-bit virtual address where bits 16 to 21 are always OFF
USER_I: 4, // use a 16-bit virtual address where bits 16 to 21 are always OFF
USER_D: 5, // use a 16-bit virtual address where bits 16 to 21 are always OFF
PROG_PHY: 6, // display the 22-bit physical address of the current bus cycle generated by the MMU
CONS_PHY: 7 // use a 22-bit physical address to perform console operations (e.g., LOAD ADRS, EXAM, & DEP)
};
/*
* To get the current state of a switch; eg::
*
* this.getSwitch(PanelPDP11.SWITCH.ENABLE)
*
* I haven't filled out this table, primarily it only needs to list switches we actually query
* (eg, non-momentary ones like 'ENABLE' and 'STEP', and 'EXAM' and 'DEP' since they have special
* "step" behavior when pressed more than once in a row). Ditto for the LED table.
*/
PanelPDP11.SWITCH = {
S0: 'S0',
DEP: 'DEP',
ENABLE: 'ENABLE',
EXAM: 'EXAM',
STEP: 'STEP'
};
PanelPDP11.LED = {
B16: 'B16',
B18: 'B18',
B22: 'B22'
};
/**
* getSW()
*
* @this {PanelPDP11}
* @return {number}
*/
PanelPDP11.prototype.getSW = function()
{
return this.regSwitches;
};
/**
* setSW(value)
*
* @this {PanelPDP11}
* @param {number} value
*/
PanelPDP11.prototype.setSW = function(value)
{
this.setSwitches(value);
};
/**
* getSwitch(name)
*
* @this {PanelPDP11}
* @param {string} name
* @return {number|undefined} 0 if switch is off ("down"), 1 if on ("up"), or undefined if unrecognized
*/
PanelPDP11.prototype.getSwitch = function(name)
{
return this.switches[name] && this.switches[name][1];
};
/**
* reset()
*
* NOTE: Since we've registered our handler with the Bus component, we will be called twice whenever
* the entire machine is reset: once when the Computer's reset() handler calls the Bus's reset() handler,
* and again when the Computer's reset() handler calls us directly. Multiple resets should be harmless.
*
* @this {PanelPDP11}
*/
PanelPDP11.prototype.reset = function()
{
/*
* Simulate a call to our stop() handler, to update the panel's 'ADDRESS' register with the current PC.
*/
this.stop();
};
/**
* setBinding(sType, sBinding, control, sValue)
*
@ -93,29 +250,83 @@ PanelPDP11.prototype.setBinding = function(sType, sBinding, control, sValue)
if (DEBUGGER && this.dbg && this.dbg.setBinding(sType, sBinding, control, sValue)) {
return true;
}
switch (sBinding) {
case "R0":
case "R1":
case "R2":
case "R3":
case "R4":
case "R5":
case "R6":
case "R7":
case "NF":
case "ZF":
case "VF":
case "CF":
case "PS":
case 'R0':
case 'R1':
case 'R2':
case 'R3':
case 'R4':
case 'R5':
case 'R6':
case 'R7':
case 'NF':
case 'ZF':
case 'VF':
case 'CF':
case 'PS':
this.bindings[sBinding] = control;
this.cLiveRegs++;
return true;
default:
if (sType == "rled") {
/*
* Square ("led") or round ("rled") LEDs are defined in machine XML files like so:
*
* <control type="rled" binding="A3" value="1" width="100%" container="center"/>
*
* Only *type* and *binding* attributes are required; if *value* is omitted, the default value is 0 ("off").
*/
if (sType == "led" || sType == "rled") {
this.bindings[sBinding] = control;
this.leds[sBinding] = sValue? 1 : 0;
this.cLiveRegs++;
return true;
}
/*
* Switches are defined in machine XML files like so:
*
* <control type="switch" binding="S3" value="1" width="100%" container="center"/>
*
* Only *type* and *binding* attributes are required; if *value* is omitted, the default value is 0 ("down").
*
* Currently, there is no XML attribute to indicate whether a switch is "momentary"; only recognized switches
* in our internal table can have that attribute.
*/
if (sType == "switch") {
/*
* Like LEDs, we allow unrecognized switches to be defined as well, but they won't do anything useful,
* since only recognized switches will have handlers that perform the appropriate operations.
*/
if (this.switches[sBinding] === undefined) {
this.switches[sBinding] = [sValue? 1 : 0, sValue? 1 : 0];
}
this.bindings[sBinding] = control;
var parent = control.parentElement || control;
parent = parent.parentElement || parent;
parent.onmousedown = function(panel, sBinding) {
return function onPressSwitch() {
panel.pressSwitch(sBinding);
};
}(this, sBinding);
parent.onmouseup = parent.onmouseout = function(panel, sBinding) {
return function onReleaseSwitch() {
panel.releaseSwitch(sBinding);
};
}(this, sBinding);
parent.ontouchstart = function(panel, sBinding) {
return function onPressSwitch(event) {
panel.pressSwitch(sBinding);
event.preventDefault();
};
}(this, sBinding);
parent.ontouchend = function(panel, sBinding) {
return function onReleaseSwitch() {
panel.releaseSwitch(sBinding);
};
}(this, sBinding);
return true;
}
return this.parent.setBinding.call(this, sType, sBinding, control, sValue);
}
};
@ -135,6 +346,12 @@ PanelPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
bus.addIOTable(this, PanelPDP11.UNIBUS_IOTABLE);
bus.addResetHandler(this.reset.bind(this));
this.displayLEDs();
this.displaySwitches();
};
/**
@ -147,7 +364,18 @@ PanelPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
*/
PanelPDP11.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) PanelPDP11.init();
if (!fRepower) {
/*
* As noted in init(), our powerUp() method gives us a second opportunity to notify any
* components that that might care (eg, CPU, Keyboard, and Debugger) that we have some controls
* they might want to use.
*/
PanelPDP11.init();
/*
* TODO: Until we implement a restore() function, all we can do is reset()
*/
this.reset();
}
return true;
};
@ -165,7 +393,66 @@ PanelPDP11.prototype.powerDown = function(fSave, fShutdown)
};
/**
* updateValue(sLabel, nValue, cch)
* displayLED(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {boolean|number} value (true or non-zero if the LED should be on, false or zero if off)
*/
PanelPDP11.prototype.displayLED = function(sBinding, value)
{
var control = this.bindings[sBinding];
if (control) {
/*
* TODO: Add support for user-definable LED colors?
*/
control.style.backgroundColor = (value? "#ff0000" : "#000000");
}
};
/**
* displayLEDs(override)
*
* @this {PanelPDP11}
* @param {boolean|number|null} [override] (true turn on all LEDs, false to turn off all LEDs, null or undefined for normal LED activity)
*/
PanelPDP11.prototype.displayLEDs = function(override)
{
for (var sBinding in this.leds) {
this.displayLED(sBinding, override != null? override : this.leds[sBinding]);
}
};
/**
* displaySwitch(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {boolean|number} value (true if the switch should be "up" (on), false if "down" (off))
*/
PanelPDP11.prototype.displaySwitch = function(sBinding, value)
{
var control = this.bindings[sBinding];
if (control) {
control.style.marginTop = (value? "0px" : "20px");
control.style.backgroundColor = (value? "#00ff00" : "#228B22");
}
};
/**
* displaySwitches()
*
* @this {PanelPDP11}
*/
PanelPDP11.prototype.displaySwitches = function()
{
for (var sBinding in this.switches) {
this.displaySwitch(sBinding, this.switches[sBinding][1]);
}
};
/**
* displayValue(sLabel, nValue, cch)
*
* This is principally for displaying register values, but in reality, it can be used to display any
* numeric value bound to the given label.
@ -175,7 +462,7 @@ PanelPDP11.prototype.powerDown = function(fSave, fShutdown)
* @param {number} nValue
* @param {number} [cch]
*/
PanelPDP11.prototype.updateValue = function(sLabel, nValue, cch)
PanelPDP11.prototype.displayValue = function(sLabel, nValue, cch)
{
if (this.bindings[sLabel]) {
if (nValue === undefined) {
@ -184,7 +471,7 @@ PanelPDP11.prototype.updateValue = function(sLabel, nValue, cch)
}
var sVal;
var nBase = this.dbg && this.dbg.nBase || 8;
if (!this.cpu.isRunning() || this.flags.displayLiveRegs) {
if (!this.cpu.isRunning() || this.fDisplayLiveRegs) {
sVal = nBase == 8? str.toOct(nValue, cch) : str.toHex(nValue, cch);
} else {
sVal = "--------".substr(0, cch || 4);
@ -200,62 +487,524 @@ PanelPDP11.prototype.updateValue = function(sLabel, nValue, cch)
};
/**
* setLED(control, f)
* pressSwitch(sBinding)
*
* @this {PanelPDP11}
* @param {Object} control is an HTML control DOM object
* @param {boolean|number} f is true if the LED represented by control should be "on", false if "off"
* @param {string} sBinding
*/
PanelPDP11.prototype.setLED = function(control, f)
PanelPDP11.prototype.pressSwitch = function(sBinding)
{
var sw = this.switches[sBinding];
/*
* TODO: Add support for user-definable LED colors
* Set the new switch value in sw[1] and then immediately display it
*/
control.style.backgroundColor = (f? "#ff0000" : "#000000");
this.displaySwitch(sBinding, (sw[1] = 1 - sw[1]));
/*
* Mark the switch as "pressed"
*/
sw[3] = true;
/*
* Call the appropriate process handler with the current switch value (sw[1])
*/
if (sw[4]) sw[4].call(this, sw[1], sw[5]);
/*
* This helps the next 'DEP' or 'EXAM' press determine if the previous press was the same,
* while also ignoring any intervening 'STEP' presses (see processStep() for why we do that).
*/
if (sBinding != PanelPDP11.SWITCH.STEP) {
this.fDeposit = (sBinding == PanelPDP11.SWITCH.DEP);
this.fExamine = (sBinding == PanelPDP11.SWITCH.EXAM);
}
};
/**
* updateLEDs(sPrefix, data, nLEDs)
* releaseSwitch(sBinding)
*
* @this {PanelPDP11}
* @param {string} sBinding
*/
PanelPDP11.prototype.releaseSwitch = function(sBinding)
{
/*
* pressSwitch() is simple: flip the switch's current value in sw[1] and marked it "pressed" in sw[3].
*
* releaseSwitch() is more complicated, because we must handle both mouseUp and mouseOut events. The first time
* we receive EITHER of those events AND the switch is marked momentary (sw[2]) AND the switch is pressed (sw[3]),
* then we must flip the switch back to its original value.
*
* Otherwise, the only thing we have to do is mark the switch as "released" (ie, set sw[3] to false).
*/
var sw = this.switches[sBinding];
if (sw[2] && sw[3]) {
/*
* Set the new switch value in sw[1] and then immediately display it
*/
this.displaySwitch(sBinding, (sw[1] = sw[0]));
/*
* Call the appropriate process handler with the current switch value (sw[1])
*/
if (sw[4]) sw[4].call(this, sw[1], sw[5]);
}
/*
* Mark the switch as "released"
*/
sw[3] = false;
};
/**
* processStart(value, index)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processStart = function(value, index)
{
if (!value && !this.cpu.isRunning()) {
/*
* TODO: Verify what the PDP-11/70 Handbook means when it says that when the 'START' switch
* is depressed, "the computer system will be cleared." I take it to mean that it performs
* the equivalent of a RESET instruction.
*/
this.bus.reset();
this.cpu.resetRegs();
/*
* The PDP-11/70 Handbook goes on to say: "If the system needs to be initialized but execution
* is not wanted, the START switch should be depressed while the HALT/ENABLE switch is in the HALT
* position."
*/
if (this.getSwitch(PanelPDP11.SWITCH.ENABLE)) {
this.cpu.startCPU();
}
}
};
/**
* processStep(value, index)
*
* If value == 1 (our initial value), then the 'STEP' switch is set to "S INST" (step one instruction);
* otherwise, it's set to "S BUS CYCLE" (step one bus cycle).
*
* However, since we can't currently support cycle-stepping, I've decided to innovate a little and
* change the meaning of this switch: the normal ("up") position means that successive 'EXAM' and 'DEP'
* operations will first add 2 to the 'ADDRESS' register, while the opposite ("down") position means
* they will first subtract 2.
*
* See processLEDTest() for more of these exciting "innovations". ;-)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processStep = function(value, index)
{
/*
* There's really nothing for us to do here, because the normal press and release handlers
* already record the state of this switch, so it can be queried as needed, using getSwitch().
*/
};
/**
* processEnable(value, index)
*
* If value == 1 (our initial value), then the 'ENABLE'/'HALT' switch is set to 'ENABLE', otherwise 'HALT'.
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processEnable = function(value, index)
{
/*
* The "down" (0) position is 'HALT', which stops the CPU; however, the "up" (1) position ('ENABLE')
* does NOT start the CPU. You must press 'CONT' to continue execution, which will either continue for
* one instruction if this switch to set to 'HALT' or indefinitely if it is set to 'ENABLE'.
*/
if (!value) {
this.cpu.stopCPU();
}
};
/**
* processContinue(value, index)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processContinue = function(value, index)
{
if (!value && !this.cpu.isRunning()) {
/*
* TODO: Technically, we're also supposed to check the 'STEP' switch to determine if we should
* step one instruction or just one cycle, but we don't currently have the ability to do the latter.
*/
if (!this.getSwitch(PanelPDP11.SWITCH.ENABLE)) {
/*
* Using the Debugger's stepCPU() function is more convenient, and has the pleasant side-effect
* of updating the debugger's display; however, not all machines with a Front Panel will necessarily
* also have the Debugger loaded.
*/
var dbg = this.dbg;
if (dbg && !dbg.isBusy(true)) {
dbg.setBusy(true);
dbg.stepCPU(0);
dbg.setBusy(false);
}
else {
/*
* For this tiny single-instruction burst, mimic what runCPU() does.
*/
try {
var nCyclesStep = this.cpu.stepCPU(1);
if (nCyclesStep > 0) {
this.cpu.updateTimers(nCyclesStep);
this.cpu.addCycles(nCyclesStep, true);
this.cpu.updateChecksum(nCyclesStep);
}
}
catch(exception) {
/*
* We assume that any numeric exception was explicitly thrown by the CPU to interrupt the
* current instruction. For all other exceptions, we attempt a stack dump.
*/
if (typeof exception != "number") {
var e = exception;
this.cpu.setError(e.stack || e.message);
}
}
}
/*
* Simulate a call to our stop() handler, to update the panel's 'ADDRESS' register with the new PC.
*/
this.stop();
/*
* Going through the normal channels (ie, the Computer's updateDisplays() interface) ensures that
* ALL updateDisplay() handlers will be called, including ours.
*
* NOTE: If we used the Debugger's stepCPU() function, then that includes a call to updateDisplay();
* unfortunately, it will have happened BEFORE we called stop() to update the 'ADDRESS' register, so
* we still need to call it again.
*/
if (this.cmp) this.cmp.updateDisplays();
}
else {
this.cpu.startCPU();
}
}
};
/**
* processDeposit(value, index)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processDeposit = function(value, index)
{
if (value && !this.cpu.isRunning()) {
if (this.fDeposit) this.advanceAddr();
var w = this.setData(this.regSwitches);
if (this.nAddrSel == PanelPDP11.ADDRSEL.CONS_PHY) {
this.bus.setWordDirect(this.regAddr, w);
} else {
/*
* TODO: This code is obviously incomplete, since it doesn't take into account the precise ADDRSEL mode.
*/
this.cpu.setWordDirect(this.regAddr, w);
}
}
};
/**
* processExamine(value, index)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processExamine = function(value, index)
{
if (!value && !this.cpu.isRunning()) {
var w;
if (this.fExamine) this.advanceAddr();
if (this.nAddrSel == PanelPDP11.ADDRSEL.CONS_PHY) {
w = this.bus.getWordDirect(this.regAddr);
} else {
/*
* TODO: This code is obviously incomplete, since it doesn't take into account the precise ADDRSEL mode.
*/
w = this.cpu.getWordDirect(this.regAddr);
}
this.setData(w);
}
};
/**
* processLoadAddr(value, index)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processLoadAddr = function(value, index)
{
if (!value && !this.cpu.isRunning()) {
this.setAddr(this.regSwitches);
}
};
/**
* processLEDTest(value, index)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} [index]
*/
PanelPDP11.prototype.processLEDTest = function(value, index)
{
if (value) {
this.fLEDTest = true;
this.displayLEDs(true);
} else {
this.fLEDTest = false;
this.displayLEDs();
/*
* This is another one of my "innovations": when you're done testing the LEDs, all the switches reset as well.
*/
this.setSwitches(0);
}
};
/**
* processSwitchReg(value, index)
*
* @this {PanelPDP11}
* @param {number} value (normally 0 or 1, but we only depend on it being zero or non-zero)
* @param {number} index
*/
PanelPDP11.prototype.processSwitchReg = function(value, index)
{
if (value) {
this.regSwitches |= 1 << index;
} else {
this.regSwitches &= ~(1 << index);
}
};
/**
* setAddr(value)
*
* @this {PanelPDP11}
* @param {number} value
* @return {number}
*/
PanelPDP11.prototype.setAddr = function(value)
{
this.regAddr = value & this.bus.nBusMask;
this.setLEDArray("A", this.regAddr, 22);
return this.regAddr;
};
/**
* advanceAddr()
*
* This should also take care of the following Front Panel behaviors when the accessing the general-purpose
* registers:
*
* 1) ADDRESS display incremented by 1 (instead of 2)
* 2) The STEP after the last register is 177700, such that the addresses are looped
*
* A third behavior is NOT emulated: preventing the ADDRESS from stepping to the first General Register (177700)
* from 177676.
*
* @this {PanelPDP11}
* @return {number}
*/
PanelPDP11.prototype.advanceAddr = function()
{
var nRegs = this.cpu.model < PDP11.MODEL_1145? 8 : 16;
var fGenRegs = (this.regAddr >= PDP11.UNIBUS.R0SET0 /*177700*/ && this.regAddr < PDP11.UNIBUS.R0SET0 + nRegs);
var inc = fGenRegs? 1 : 2;
var mask = fGenRegs? 0xf : this.bus.nBusMask;
if (!this.getSwitch(PanelPDP11.SWITCH.STEP)) inc = -inc;
this.regAddr = (this.regAddr & ~mask) | ((this.regAddr + inc) & mask);
this.setLEDArray("A", this.regAddr, 22);
return this.regAddr;
};
/**
* setData(value)
*
* @this {PanelPDP11}
* @param {number} value
* @return {number}
*/
PanelPDP11.prototype.setData = function(value)
{
this.regData = value & 0xffff;
this.setLEDArray("D", this.regData, 16);
return this.regData;
};
/**
* setLED(sBinding, value)
*
* @this {PanelPDP11}
* @param {string} sBinding
* @param {number} value
* @return {number}
*/
PanelPDP11.prototype.setLED = function(sBinding, value)
{
this.leds[sBinding] = value;
if (!this.fLEDTest) this.displayLED(sBinding, value);
return value;
};
/**
* setLEDArray(sPrefix, value, nLEDs)
*
* @this {PanelPDP11}
* @param {string} sPrefix
* @param {number} data
* @param {number} value
* @param {number} nLEDs
*/
PanelPDP11.prototype.updateLEDs = function(sPrefix, data, nLEDs)
PanelPDP11.prototype.setLEDArray = function(sPrefix, value, nLEDs)
{
for (var i = 0; i < nLEDs; i++) {
var id = sPrefix + i;
var control = this.bindings[id];
if (control) {
this.setLED(control, data & (1 << i));
var sBinding = sPrefix + i;
this.setLED(sBinding, value & (1 << i));
}
};
/**
* hasSwitches(value)
*
* @this {PanelPDP11}
* @return {boolean}
*/
PanelPDP11.prototype.hasSwitches = function()
{
return this.bindings[PanelPDP11.SWITCH.S0] !== undefined;
};
/**
* setSwitches(value)
*
* @this {PanelPDP11}
* @param {number} value
*/
PanelPDP11.prototype.setSwitches = function(value)
{
if (this.hasSwitches()) {
this.regSwitches = value;
for (var i = 0; i < 22; i++) {
this.switches['S'+i][1] = (value & (1 << i))? 1 : 0;
}
this.displaySwitches();
}
};
/**
* stop(ms, nCycles)
*
* This is a notification handler, called by the Computer, to inform us the CPU has now stopped.
*
* @this {PanelPDP11}
* @param {number} [ms]
* @param {number} [nCycles]
*/
PanelPDP11.prototype.stop = function(ms, nCycles)
{
this.setAddr(this.cpu.regsGen[7]);
/*
* TODO: Consider an option to call setData() with the current opcode as well; presumably that wouldn't be
* normal Front Panel behavior, but it could be useful for debugging.
*/
};
/**
* updateDisplay(nUpdate)
*
* Called by the Computer component at intervals to update registers, LEDs, etc.
*
* @this {PanelPDP11}
* @param {number} [nUpdate] (< 0 for forced, > 0 for periodic, undefined otherwise)
*/
PanelPDP11.prototype.updateDisplay = function(nUpdate)
{
if (this.cLiveRegs) {
if (nUpdate < 0 || !this.cpu.isRunning() || this.fDisplayLiveRegs) {
/*
* We arbitrarily separate the display elements into two categories: cheap and expensive.
*
* LEDs are considered cheap, register displays are not. So we'll skip the latter if this
* is a periodic update AND our periodic update counter hasn't reached the periodic update limit.
*/
if (!(nUpdate > 0 && (this.nPeriodicCount += nUpdate) < this.nPeriodicLimit)) {
for (var i = 0; i < this.cpu.regsGen.length; i++) {
this.displayValue('R'+i, this.cpu.regsGen[i]);
}
var regPSW = this.cpu.getPSW();
this.displayValue("PS", regPSW);
this.displayValue("NF", (regPSW & PDP11.PSW.NF)? 1 : 0, 1);
this.displayValue("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
this.displayValue("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
this.displayValue("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
this.nPeriodicCount = 0;
}
this.setLEDArray("D", this.regData, 16);
this.setLEDArray("A", this.regAddr, 22);
/*
* Set bit to 1 (22-bit), 2 (18-bit), or 4 (16-bit)
*/
var bit = this.cpu.mmuEnable? ((this.cpu.regMMR3 & PDP11.MMR3.MMU_22BIT)? 1 : 2) : 4;
this.setLED(PanelPDP11.LED.B22, bit & 1);
this.setLED(PanelPDP11.LED.B18, bit & 2);
this.setLED(PanelPDP11.LED.B16, bit & 4);
}
}
};
/**
* updateStatus(fForce)
* readCNSW(addr)
*
* If addr is set, then this a normal read, so we should return normal results (ie, switches);
* if addr is NOT set, then this is a read-before-write, so we must return the value being updated.
*
* @this {PanelPDP11}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
* @param {number} addr (eg, PDP11.UNIBUS.CNSW or 177570)
* @return {number}
*/
PanelPDP11.prototype.updateStatus = function(fForce)
PanelPDP11.prototype.readCNSW = function(addr)
{
if (this.cLiveRegs) {
if (fForce || !this.cpu.isRunning() || this.flags.displayLiveRegs) {
for (var i = 0; i < this.cpu.regsGen.length; i++) {
this.updateValue('R'+i, this.cpu.regsGen[i]);
}
var regPSW = this.cpu.getPSW();
this.updateValue("PS", regPSW);
this.updateValue("NF", (regPSW & PDP11.PSW.NF)? 1 : 0, 1);
this.updateValue("ZF", (regPSW & PDP11.PSW.ZF)? 1 : 0, 1);
this.updateValue("VF", (regPSW & PDP11.PSW.VF)? 1 : 0, 1);
this.updateValue("CF", (regPSW & PDP11.PSW.CF)? 1 : 0, 1);
this.updateLEDs("D", this.cpu.regsGen[0], 16);
this.updateLEDs("A", this.cpu.regsGen[7], 22);
}
}
return (addr? this.regSwitches : this.regData) & 0xffff;
};
/**
* writeCNSW(value, addr)
*
* @this {PanelPDP11}
* @param {number} value
* @param {number} addr (eg, PDP11.UNIBUS.CNSW or 177570)
*/
PanelPDP11.prototype.writeCNSW = function(value, addr)
{
this.regData = value;
};
PanelPDP11.UNIBUS_IOTABLE = {
[PDP11.UNIBUS.CNSW]: /* 177570 */ [null, null, PanelPDP11.prototype.readCNSW, PanelPDP11.prototype.writeCNSW, "CNSW"]
};
/**

View file

@ -176,20 +176,20 @@ RAMPDP11.prototype.doneLoad = function(sURL, sData, nErrorCode)
{
if (nErrorCode) {
this.notice("Unable to load RAM resource (error " + nErrorCode + ": " + sURL + ")");
return;
}
Component.addMachineResource(this.idMachine, sURL, sData);
var resource = web.parseMemoryResource(sURL, sData);
if (resource) {
this.abInit = resource.aBytes;
this.aSymbols = resource.aSymbols;
if (this.addrLoad == null) this.addrLoad = resource.addrLoad;
if (this.addrExec == null) this.addrExec = resource.addrExec;
} else {
this.sFilePath = null;
}
else {
Component.addMachineResource(this.idMachine, sURL, sData);
var resource = web.parseMemoryResource(sURL, sData);
if (resource) {
this.abInit = resource.aBytes;
this.aSymbols = resource.aSymbols;
if (this.addrLoad == null) this.addrLoad = resource.addrLoad;
if (this.addrExec == null) this.addrExec = resource.addrExec;
} else {
this.sFilePath = null;
}
}
this.initRAM();
};
@ -239,7 +239,12 @@ RAMPDP11.prototype.initRAM = function()
RAMPDP11.prototype.reset = function()
{
if (this.fAllocated) {
this.bus.zeroMemory(this.addrRAM, this.sizeRAM);
/*
* TODO: Add a configuration parameter for selecting the byte pattern on reset?
* Note that when memory blocks are originally created, they are currently always
* zero-initialized, so this would only affect resets.
*/
this.bus.zeroMemory(this.addrRAM, this.sizeRAM, 0);
if (this.abInit) {
this.loadImage(this.abInit, this.addrLoad, this.addrExec, this.addrRAM, true);
}

View file

@ -1,85 +0,0 @@
/**
* @fileoverview Implements RK11 device support.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @copyright © Jeff Parsons 2012-2016
*
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
*
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.4 written by Paul Nankervis
* (paulnank@hotmail.com) as of September 2016 at <http://skn.noip.me/pdp11/pdp11.html>. This code
* may be used freely provided the original authors are acknowledged in any modified source code.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCjs. If not,
* see <http://www.gnu.org/licenses/gpl.html>.
*
* You are required to include the above copyright notice in every modified copy of this work
* and to display that copyright notice when the software starts running; see COPYRIGHT in
* <http://pcjs.org/modules/shared/lib/defines.js>.
*
* Some PCjs files also attempt to load external resource files, such as character-image files,
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
* for purposes of the GNU General Public License, and the author does not claim any copyright
* as to their contents.
*/
"use strict";
if (NODE) {
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var State = require("../../shared/lib/state");
var BusPDP11 = require("./bus");
}
/**
* RK11(parmsDevice)
*
* @constructor
* @extends Component
* @param {Object} parmsRK11
*/
function RK11(parmsRK11)
{
Component.call(this, "RK11", parmsRK11, RK11);
this.rkds = 0x9C0;/*04700*/ // 017777400 Drive Status
this.rker = 0; // 017777402 Error Register
this.rkcs = 0x80; /*0200*/ // 017777404 Control Status
this.rkwc = 0; // 017777406 Word Count
this.rkba = 0; // 017777410 Bus Address
this.rkda = 0; // 017777412 Disk Address
this.rkdb = 0; // 017777416 Data Buffer
this.meta = [];
this.TRACKS = [406, 406, 406, 406];
this.SECTORS = [12, 12, 12, 12];
}
Component.subclass(RK11);
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {DevicePDP11}
* @param {ComputerPDP11} cmp
* @param {BusPDP11} bus
* @param {CPUStatePDP11} cpu
* @param {DebuggerPDP11} dbg
*/
RK11.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.setReady();
};
if (NODE) module.exports = RK11;

1237
modules/pdp11/lib/rl11.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -33,12 +33,14 @@
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
var PDP11 = require("./defines");
var MemoryPDP11 = require("./memory");
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
var PDP11 = require("./defines");
var BusPDP11 = require("./bus");
var MemoryPDP11 = require("./memory");
var MessagesPDP11 = require("./messages");
}
/**
@ -51,11 +53,11 @@ if (NODE) {
* alias: physical alias address (null if none)
* file: name of ROM data file
*
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND the
* ROM data file has finished loading (see doneLoad()).
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND
* the ROM data file has finished loading (see doneLoad()).
*
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
* is the ROM you expected.
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM
* you received is the ROM you expected.
*
* @constructor
* @extends Component
@ -70,6 +72,7 @@ function ROMPDP11(parmsROM)
this.addrROM = parmsROM['addr'];
this.sizeROM = parmsROM['size'];
this.fRetainROM = false;
/*
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
@ -191,18 +194,18 @@ ROMPDP11.prototype.doneLoad = function(sURL, sData, nErrorCode)
{
if (nErrorCode) {
this.notice("Unable to load ROM resource (error " + nErrorCode + ": " + sURL + ")");
return;
}
Component.addMachineResource(this.idMachine, sURL, sData);
var resource = web.parseMemoryResource(sURL, sData);
if (resource) {
this.abInit = resource.aBytes;
this.aSymbols = resource.aSymbols;
} else {
this.sFilePath = null;
}
else {
Component.addMachineResource(this.idMachine, sURL, sData);
var resource = web.parseMemoryResource(sURL, sData);
if (resource) {
this.abInit = resource.aBytes;
this.aSymbols = resource.aSymbols;
} else {
this.sFilePath = null;
}
}
this.initROM();
};
@ -260,7 +263,9 @@ ROMPDP11.prototype.initROM = function()
* whether they're ROM or RAM. However, the only way to modify a machine's ROM is with the Debugger,
* and Debugger users should know better.
*/
delete this.abInit;
if (!this.fRetainROM) {
delete this.abInit;
}
}
}
this.setReady();
@ -276,7 +281,24 @@ ROMPDP11.prototype.initROM = function()
*/
ROMPDP11.prototype.addROM = function(addr)
{
if (this.bus.addMemory(addr, this.sizeROM, MemoryPDP11.TYPE.ROM)) {
this.status(this.sizeROM + "-byte ROM at " + str.toOct(addr));
if (addr >= BusPDP11.IOPAGE_16BIT && addr < BusPDP11.IOPAGE_16BIT + BusPDP11.IOPAGE_LENGTH) {
/*
* This code has been added as a work-around to effectively allow us to install small ROMs into portions
* of the IOPAGE address space, by installing I/O handlers for the entire range that return the corresponding
* bytes of the current ROM image on reads, and ignore any writes (which I'm only assuming is how a typical
* ROM "device" deals with writes; if we remove the write handler, then writes will fault).
*/
var IOTable = {
[addr]: [ROMPDP11.prototype.readROMByte, ROMPDP11.prototype.writeROMByte, null, null, null, this.sizeROM >> 1]
};
if (this.bus.addIOTable(this, IOTable, MessagesPDP11.ROM, this.idComponent)) {
this.fRetainROM = true;
return true;
}
}
else if (this.bus.addMemory(addr, this.sizeROM, MemoryPDP11.TYPE.ROM)) {
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexLong(addr) + " (" + str.toHexLong(this.abInit.length) + " bytes)");
var i;
for (i = 0; i < this.abInit.length; i++) {
@ -284,6 +306,7 @@ ROMPDP11.prototype.addROM = function(addr)
}
return true;
}
/*
* We don't need to report an error here, because addMemory() already takes care of that.
*/
@ -309,6 +332,36 @@ ROMPDP11.prototype.cloneROM = function(addr)
this.bus.setMemoryBlocks(addr, this.sizeROM, aBlocks);
};
/**
* readROMByte(addr)
*
* @this {ROMPDP11}
* @param {number} addr
* @return {number}
*/
ROMPDP11.prototype.readROMByte = function(addr)
{
var i = (addr - this.addrROM);
return this.abInit[i];
};
/**
* writeROMByte(data, addr)
*
* This handler exists simply to ignore any writes, so that they don't cause faults.
*
* TODO: Another possible use for this would be to allow the Debugger to alter ROM contents,
* if the Debugger were to provide an interface indicating whether or not it was responsible
* for this write.
*
* @this {ROMPDP11}
* @param {number} data
* @param {number} addr
*/
ROMPDP11.prototype.writeROMByte = function(data, addr)
{
};
/**
* ROMPDP11.init()
*

View file

@ -1077,6 +1077,15 @@ if (!Array.prototype.indexOf) {
}
}
/*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
*/
if (!Array.isArray) {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
/*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
*/

View file

@ -681,7 +681,7 @@ if (DEBUGGER) {
var s;
switch(this.nBase) {
case 8:
s = str.toOct(n, nBytes * 3);
s = str.toOct(n, nBytes * 3 - (nBytes > 2? 1 : 0));
break;
case 10:
s = n.toString();

View file

@ -70,20 +70,43 @@ var DiskAPI = {
/*
* Common (supported) diskette formats
*
* For no particular reason that I can recall, each entry in DISK_FORMATS is an array of values in "CHS" order:
*
* [# 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.
*/
DiskAPI.DISKETTE_FORMATS = {
163840: [40,1,8], // media type 0xFE: 40 cylinders, 1 head (single-sided), 8 sectors/track, ( 320 total sectors x 512 bytes/sector == 163840)
184320: [40,1,9], // media type 0xFC: 40 cylinders, 1 head (single-sided), 9 sectors/track, ( 360 total sectors x 512 bytes/sector == 184320)
327680: [40,2,8], // media type 0xFF: 40 cylinders, 2 heads (double-sided), 8 sectors/track, ( 640 total sectors x 512 bytes/sector == 327680)
368640: [40,2,9], // media type 0xFD: 40 cylinders, 2 heads (double-sided), 9 sectors/track, ( 720 total sectors x 512 bytes/sector == 368640)
737280: [80,2,9], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 9 sectors/track, (1440 total sectors x 512 bytes/sector == 737280)
1228800: [80,2,15], // media type 0xF9: 80 cylinders, 2 heads (double-sided), 15 sectors/track, (2400 total sectors x 512 bytes/sector == 1228800)
1474560: [80,2,18], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 18 sectors/track, (2880 total sectors x 512 bytes/sector == 1474560)
2949120: [80,2,36], // media type 0xF0: 80 cylinders, 2 heads (double-sided), 36 sectors/track, (5760 total sectors x 512 bytes/sector == 2949120)
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)
/*
* 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)
21368320:[615,4,17], // PC AT 20Mb hard drive (type 2)
/*
* Assorted DEC disk pack formats.
*/
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
};
/*
* TODO: Eventually, our tools will need to support looking up disk formats by "model" rather than by raw disk size,
* because obviously multiple disk geometries can yield the same raw disk size. For each conflict that arises, I'll
* probably create a fake (approximate) disk size entry above, and then create a mapping to that approximate size below.
*/
DiskAPI.DISK_MODELS = {
"RL01": 5242880,
"RL02": 10485760
};
DiskAPI.MBR = {

View file

@ -202,9 +202,9 @@ web.getResource = function(sURL, dataPost, fAsync, done)
if (DEBUG) {
/*
* The larger resources that we put on archive.pcjs.org should also be available locally...
* The larger resources we put on archive.pcjs.org should also be available locally...
*/
sURL = sURL.replace("http://archive.pcjs.org", "");
sURL = sURL.replace(/^http:\/\/archive.pcjs.org(\/.*)\/([^\/]*)$/, "$1/archive/$2");
}
if (NODE) {
@ -286,6 +286,7 @@ web.getResource = function(sURL, dataPost, fAsync, done)
/**
* parseMemoryResource(sURL, sData)
*
* @param {string} sURL
* @param {string} sData
* @return {Object|null} (resource)
*/

View file

@ -129,16 +129,31 @@
color: #ffffff;
background-color: #404040;
}
.pcjs-tripled {
.pcjs-triplet {
padding: 1px;
}
.pcjs-tripled-label {
.pcjs-ledlbl {
text-align: center;
font-size: 40%;
background-color: #000000;
}
.pcjs-ledlbl0 {
text-align: right;
padding: 8px;
font-size: 50%;
background-color: #8d4076;
}
.pcjs-ledlbl1 {
text-align: right;
font-size: 50%;
background-color: #d83662;
}
.pcjs-ledpad {
text-align: center;
font-size: x-small;
line-height: 32px;
background-color: #000000;
border-bottom-left-radius: 20%;
border-bottom-right-radius: 20%;
}
.pcjs-led {
float: left;
@ -159,6 +174,27 @@
text-align: center;
vertical-align: middle;
background-color: #ff0000;
max-width: 50%;
max-height: 50%;
}
.pcjs-swlbl {
text-align: center;
font-size: 40%;
line-height: 16px;
background-color: #000000;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
}
.pcjs-swpad {
height: 32px;
background-color: #000000;
}
.pcjs-switch {
height: 10px;
width: 28px;
margin-top: 0%;
max-width: 90%;
background-color: #00ff00;
}
.pcjs-screen {
clear: both;

View file

@ -422,7 +422,10 @@
</form>
</xsl:when>
<xsl:when test="@type = 'led' or @type = 'rled'">
<div class="{$APPCLASS}-binding {$CSSCLASS}-{@type}" data-value="{{{$type},{$binding}}}" style="display:inline-block;"><xsl:value-of select="."/></div>
<div class="{$APPCLASS}-binding {$CSSCLASS}-{@type}" data-value="{{{$type},{$binding},{$value}}}" style="display:inline-block;"><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'switch'">
<div class="{$APPCLASS}-binding {$CSSCLASS}-{@type}" data-value="{{{$type},{$binding},{$value}}}" style="display:inline-block;"><xsl:value-of select="."/></div>
</xsl:when>
<xsl:when test="@type = 'progress'">
<div class="{$APPCLASS}-binding {$CSSCLASS}-{@type}" style="-webkit-user-select:none;{$border}{$width}{$height}{$fontsize}{$style}" data-value="{{{$type},{$binding},{$value}}}">