Eliminated the NO_FLAGS hack, which was doubly-annoying because its sole purpose was to prevent writes to ONE address -- the PSW -- from modifying any flags; the proper solution is to simply ensure that all flag updates occur before the destination is written

This commit is contained in:
Jeff Parsons 2016-12-01 21:12:18 -08:00 committed by Jeff Parsons
commit a248b177fd
7 changed files with 630 additions and 662 deletions

View file

@ -913,6 +913,55 @@ BusPDP11.prototype.getByte = function(addr)
return this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
};
/**
* getWord(addr)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @return {number} word (16-bit) value at that address
*/
BusPDP11.prototype.getWord = function(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
}
return this.aMemBlocks[iBlock].readWord(off, addr);
};
/**
* setByte(addr, b)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} b is the byte (8-bit) value to write
*/
BusPDP11.prototype.setByte = function(addr, b)
{
this.assert(!(b & ~0xff));
this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b, addr);
};
/**
* setWord(addr, w)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} w is the word (16-bit) value to write
*/
BusPDP11.prototype.setWord = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
return;
}
this.aMemBlocks[iBlock].writeWord(off, w, addr);
};
/**
* getBlockDirect(addr)
*
@ -943,23 +992,6 @@ BusPDP11.prototype.getByteDirect = function(addr)
return b;
};
/**
* getWord(addr)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @return {number} word (16-bit) value at that address
*/
BusPDP11.prototype.getWord = function(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
}
return this.aMemBlocks[iBlock].readWord(off, addr);
};
/**
* getWordDirect(addr)
*
@ -985,18 +1017,6 @@ BusPDP11.prototype.getWordDirect = function(addr)
return w;
};
/**
* setByte(addr, b)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
*/
BusPDP11.prototype.setByte = function(addr, b)
{
this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
};
/**
* setByteDirect(addr, b)
*
@ -1014,25 +1034,6 @@ BusPDP11.prototype.setByteDirect = function(addr, b)
this.nDisableFaults--;
};
/**
* setWord(addr, w)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
*/
BusPDP11.prototype.setWord = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
return;
}
this.aMemBlocks[iBlock].writeWord(off, w & 0xffff, addr);
};
/**
* setWordDirect(addr, w)
*

View file

@ -875,7 +875,7 @@ PDP11.opBVS = function(opCode)
*/
PDP11.opCLR = function(opCode)
{
this.updateAllFlags(this.writeDstWord(opCode, 0));
this.writeDstWord(opCode, 0, this.updateAllFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};
@ -887,7 +887,7 @@ PDP11.opCLR = function(opCode)
*/
PDP11.opCLRB = function(opCode)
{
this.updateAllFlags(this.writeDstByte(opCode, 0, PDP11.WRITE.BYTE));
this.writeDstByte(opCode, 0, PDP11.WRITE.BYTE, this.updateAllFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};
@ -1283,8 +1283,8 @@ PDP11.opMARK = function(opCode)
PDP11.opMFPD = function(opCode)
{
var data = this.readWordFromPrevSpace(opCode, PDP11.ACCESS.DSPACE);
this.pushWord(data);
this.updateNZVFlags(data);
this.pushWord(data);
this.nStepCycles -= (10 + 1);
};
@ -1297,8 +1297,8 @@ PDP11.opMFPD = function(opCode)
PDP11.opMFPI = function(opCode)
{
var data = this.readWordFromPrevSpace(opCode, PDP11.ACCESS.ISPACE);
this.pushWord(data);
this.updateNZVFlags(data);
this.pushWord(data);
this.nStepCycles -= (10 + 1);
};
@ -1346,7 +1346,7 @@ PDP11.opMOV = function(opCode)
*/
var data = this.readSrcWord(opCode);
this.nSnapCycles = this.nStepCycles;
this.updateNZVFlags(this.writeDstWord(opCode, data));
this.writeDstWord(opCode, data, this.updateNZVFlags);
this.nStepCycles = this.nSnapCycles - PDP11.MOV_CYCLES[(this.srcMode? 8 : 0) + this.dstMode] + (this.dstReg == 7 && !this.dstMode? 2 : 0);
};
@ -1359,7 +1359,7 @@ PDP11.opMOV = function(opCode)
PDP11.opMOVB = function(opCode)
{
var data = this.readSrcByte(opCode);
this.updateNZVFlags(this.writeDstByte(opCode, data, PDP11.WRITE.SBYTE) << 8);
this.writeDstByte(opCode, data, PDP11.WRITE.SBYTE, this.updateNZVFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) + (this.srcReg && this.dstReg >= 6? 1 : 0) : (this.srcMode? (3 + 2) : (2 + 1)) + (this.dstReg == 7? 2 : 0));
};
@ -1381,8 +1381,8 @@ PDP11.opMTPD = function(opCode)
*/
var data = this.popWord();
this.nSnapCycles = this.nStepCycles;
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.DSPACE, data);
this.updateNZVFlags(data);
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.DSPACE, data);
this.nStepCycles = this.nSnapCycles - PDP11.MTP_CYCLES[this.dstMode];
};
@ -1400,8 +1400,8 @@ PDP11.opMTPI = function(opCode)
*/
var data = this.popWord();
this.nSnapCycles = this.nStepCycles;
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.ISPACE, data);
this.updateNZVFlags(data);
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.ISPACE, data);
this.nStepCycles = this.nSnapCycles - PDP11.MTP_CYCLES[this.dstMode];
};
@ -1775,7 +1775,7 @@ PDP11.opSWAB = function(opCode)
*/
PDP11.opSXT = function(opCode)
{
this.updateNZVFlags(this.writeDstWord(opCode, this.getNF()? 0xffff : 0));
this.writeDstWord(opCode, this.getNF()? 0xffff : 0, this.updateNZVFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};

View file

@ -1188,10 +1188,8 @@ CPUStatePDP11.prototype.setPIR = function(newPIR)
*/
CPUStatePDP11.prototype.updateNZVFlags = function(result)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
this.flagV = 0;
}
this.flagN = this.flagZ = result;
this.flagV = 0;
};
/**
@ -1204,10 +1202,8 @@ CPUStatePDP11.prototype.updateNZVFlags = function(result)
*/
CPUStatePDP11.prototype.updateNZVCFlags = function(result)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
this.flagV = this.flagC = 0;
}
this.flagN = this.flagZ = result;
this.flagV = this.flagC = 0;
};
/**
@ -1221,10 +1217,8 @@ CPUStatePDP11.prototype.updateNZVCFlags = function(result)
*/
CPUStatePDP11.prototype.updateAllFlags = function(result, overflow)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = overflow || 0;
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = overflow || 0;
};
/**
@ -1237,10 +1231,8 @@ CPUStatePDP11.prototype.updateAllFlags = function(result, overflow)
*/
CPUStatePDP11.prototype.updateAddFlags = function(result, src, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ result) & (dst ^ result);
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ result) & (dst ^ result);
};
/**
@ -1254,11 +1246,11 @@ CPUStatePDP11.prototype.updateAddFlags = function(result, src, dst)
*/
CPUStatePDP11.prototype.updateDecFlags = function(result, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
// Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation
this.flagV = (/* src ^ */ dst) & (dst ^ result);
}
this.flagN = this.flagZ = result;
/*
* Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation.
*/
this.flagV = (/* src ^ */ dst) & (dst ^ result);
};
/**
@ -1272,11 +1264,11 @@ CPUStatePDP11.prototype.updateDecFlags = function(result, dst)
*/
CPUStatePDP11.prototype.updateIncFlags = function(result, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
// Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation
this.flagV = (/* src ^ */ result) & (dst ^ result);
}
this.flagN = this.flagZ = result;
/*
* Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation.
*/
this.flagV = (/* src ^ */ result) & (dst ^ result);
};
/**
@ -1287,23 +1279,10 @@ CPUStatePDP11.prototype.updateIncFlags = function(result, dst)
*/
CPUStatePDP11.prototype.updateMulFlags = function(result)
{
/*
* NOTE: Technically, the MUL instruction doesn't need to worry about NO_FLAGS, because that instruction
* doesn't write to the bus, and therefore can't modify the PSW directly. But it doesn't hurt to be consistent.
*
* TODO: Conduct a review of all opcode handlers, because one possible alternative to all this NO_FLAGS nonsense
* would be to pass the appropriate flag update function to the writeDstByte()/writeDstWord() functions, and
* have them update the flags BEFORE the write occurs, thus allowing any subsequent write to the PSW to be honored.
*
* That already happens automatically with the updateDstByte()/updateDstWord() functions, since generally the
* specified modify function also updates the flags BEFORE the write occurs.
*/
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = result >> 16;
this.flagZ = this.flagN | result;
this.flagV = 0;
this.flagC = (result < -32768 || result > 32767)? 0x10000 : 0;
}
this.flagN = result >> 16;
this.flagZ = this.flagN | result;
this.flagV = 0;
this.flagC = (result < -32768 || result > 32767)? 0x10000 : 0;
};
/**
@ -1314,10 +1293,8 @@ CPUStatePDP11.prototype.updateMulFlags = function(result)
*/
CPUStatePDP11.prototype.updateShiftFlags = function(result)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = this.flagN ^ (this.flagC >> 1);
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = this.flagN ^ (this.flagC >> 1);
};
/**
@ -1333,10 +1310,8 @@ CPUStatePDP11.prototype.updateShiftFlags = function(result)
*/
CPUStatePDP11.prototype.updateSubFlags = function(result, src, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ dst) & (dst ^ result);
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ dst) & (dst ^ result);
};
/**
@ -2174,7 +2149,8 @@ CPUStatePDP11.prototype.readWordFromVirtual = function(addrVirtual)
CPUStatePDP11.prototype.writeWordToPhysical = function(addr, data)
{
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
this.bus.setWord(this.addrLast = addr, data & 0xffff);
this.assert(!(data & ~0xffff));
this.bus.setWord(this.addrLast = addr, data);
};
/**
@ -2419,10 +2395,6 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
/*
* TODO: If callers are careful about masking data, then we don't need to mask it here or in bus.setWord().
* We've eliminated the 0xffff data mask here, but bus.setWord() is still masking.
*/
this.assert(data < 0 && data >= -8 || !(data & ~0xffff));
if (!mode) {
@ -2434,7 +2406,7 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
};
/**
* writeDstByte(opCode, data, writeFlags)
* writeDstByte(opCode, data, writeFlags, fnFlags)
*
* Used whenever the DST operand (as described by opCode) does NOT need to be read before writing.
*
@ -2442,9 +2414,9 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
* @param {number} opCode
* @param {number} data
* @param {number} writeFlags (WRITE.BYTE aka 0xff, or WRITE.SBYTE aka 0xffff)
* @return {number}
* @param {function(number)} fnFlags
*/
CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags)
CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags, fnFlags)
{
this.assert(writeFlags);
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
@ -2463,41 +2435,39 @@ CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags)
data = (data < 0? (this.regsGen[-data-1] & 0xff): data);
this.regsGen[reg] = (this.regsGen[reg] & ~writeFlags) | (((data << 24) >> 24) & writeFlags);
}
fnFlags.call(this, data << 8);
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_BYTE);
this.writeByteToPhysical(addr, (data = data < 0? (this.regsGen[-data-1] & 0xff): data));
fnFlags.call(this, (data = data < 0? (this.regsGen[-data-1] & 0xff) : data) << 8);
this.writeByteToPhysical(addr, data);
}
return data;
};
/**
* writeDstWord(opCode, data)
* writeDstWord(opCode, data, fnFlags)
*
* Used whenever the DST operand (as described by opCode) does NOT need to be read before writing.
*
* @this {CPUStatePDP11}
* @param {number} opCode
* @param {number} data
* @return {number}
* @param {function(number)} fnFlags
*/
CPUStatePDP11.prototype.writeDstWord = function(opCode, data)
CPUStatePDP11.prototype.writeDstWord = function(opCode, data, fnFlags)
{
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
/*
* TODO: If callers are careful about masking data, then we don't need to mask it here or in bus.setWord().
* We've eliminated the 0xffff data mask here, but bus.setWord() is still masking.
*/
this.assert(data < 0 && data >= -8 || !(data & ~0xffff));
if (!mode) {
this.regsGen[reg] = (data = (data < 0? this.regsGen[-data-1] : data));
this.regsGen[reg] = (data = data < 0? this.regsGen[-data-1] : data);
fnFlags.call(this, data);
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_WORD);
this.bus.setWord(addr, (data = (data < 0? this.regsGen[-data-1] : data)));
fnFlags.call(this, (data = data < 0? this.regsGen[-data-1] : data));
this.bus.setWord(addr, data);
}
return data;
};
/**

View file

@ -274,7 +274,6 @@ var PDP11 = {
TRAP_MASK: 0x0070,
TRAP_LAST: 0x0080, // set if last operation was a trap (see trapLast for the vector, and trapReason for the reason)
TRAP_RED: 0x0100, // set whenever a RED trap occurs, used to catch double RED traps (time to PANIC)
NO_FLAGS: 0x0200 // set whenever the PSW is written directly, requiring all updateXXXFlags() functions to leave flags unchanged
},
/*
* Opcode reg (opcode bits 2-0)

View file

@ -1065,7 +1065,6 @@ DevicePDP11.prototype.writePSW = function(data, addr)
*/
var maskDisallowed = PDP11.PSW.UNUSED;
this.cpu.setPSW((data & ~maskDisallowed) | (this.cpu.getPSW() & maskDisallowed));
this.cpu.opFlags |= PDP11.OPFLAG.NO_FLAGS;
};
/**