Merge branch 'next-release'
This commit is contained in:
commit
505a429d5b
350 changed files with 21692 additions and 2847 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
1233
modules/pdp11/lib/disk.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
1237
modules/pdp11/lib/rl11.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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()
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}}}">
|
||||
|
|
|
|||
Loading…
Reference in a new issue