Fixed the memory clear "toggle-in", by returning the correct amount of installed RAM, and passing properly masked addresses to all the I/O handlers

This commit is contained in:
Jeff 2016-11-04 17:09:50 -07:00 committed by Jeff Parsons
commit 0cf11c0b01
8 changed files with 605 additions and 511 deletions

View file

@ -94,7 +94,7 @@ function BusPDP11(parmsBus, cpu, dbg)
* as a result, our IOController functions assume that all incoming offsets are within a single 8Kb block.
*/
this.addrTotal = 1 << this.nBusWidth;
this.nBusLimit = this.nBusMask = (this.addrTotal - 1);
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;
@ -135,6 +135,7 @@ function BusPDP11(parmsBus, cpu, dbg)
this.fIOBreakAll = false;
this.nDisableFaults = 0;
this.fFault = false;
this.cbRAM = 0;
/*
* Array of RESET notification handlers registered by Device components.
@ -242,21 +243,28 @@ 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;
}
}
}
@ -288,12 +296,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;
}
/*
@ -306,15 +321,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
@ -327,9 +342,9 @@ 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;
}
}
@ -359,11 +374,18 @@ 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) {
@ -393,13 +415,20 @@ 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;
}
}
@ -458,7 +487,7 @@ BusPDP11.prototype.setIOPageRange = function(nRange)
if (nRange) {
this.nIOPageRange = nRange;
addr = (1 << nRange);
this.nBusLimit = this.nBusMask = (addr - 1);
this.nBusMask = (addr - 1);
addr -= BusPDP11.IOPAGE_LENGTH;
this.aIOPrevBlocks = this.getMemoryBlocks(addr, BusPDP11.IOPAGE_LENGTH);
if (this.aIOPageBlocks) {
@ -633,6 +662,9 @@ BusPDP11.prototype.addMemory = function(addr, size, type, controller)
}
if (sizeLeft <= 0) {
if (type == MemoryPDP11.TYPE.RAM && !this.cbRAM) {
this.cbRAM += size;
}
this.status(str.toDec(size / 1024) + "Kb " + MemoryPDP11.TYPE_NAMES[type] + " at " + str.toOct(addr));
return true;
}
@ -1203,6 +1235,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 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)
*
@ -1331,7 +1384,7 @@ BusPDP11.prototype.fault = function(addr, err, access)
* @this {BusPDP11}
* @return {boolean}
*/
BusPDP11.prototype.checkFault= function()
BusPDP11.prototype.checkFault = function()
{
var f = this.fFault;
this.fFault = false;

View file

@ -278,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}
*/
@ -1257,6 +1260,11 @@ CPUStatePDP11.prototype.getTrapStatus = function()
* 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} addr
* @return {number}
@ -1295,15 +1303,33 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
* 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
* 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

View file

@ -770,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}
@ -2013,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;
}
}
}

View file

@ -518,7 +518,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)

View file

@ -38,6 +38,7 @@ 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");
@ -854,13 +855,24 @@ DevicePDP11.prototype.writeCTRL = function(data, addr)
/**
* 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;
return addr == PDP11.UNIBUS.LSIZE? ((this.bus.getMemorySize(MemoryPDP11.TYPE.RAM) >> 6) - 1) : 0;
};
/**